CSharpGL(29)初步封装Texture和Framebuffer
CSharpGL(29)初步封装Texture和Framebuffer
Texture和Framebuffer
Texture和Framebuffer是OpenGL进行3D渲染高级效果必不可少的利器。有了Texture和Framebuffer就可以实现体渲染(Volume Rendering)等效果。现在到了对Texture和Framebuffer的创建、修改、使用进行封装的时候。

下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
封装Texture
过程式的Texture
首先观察一下平时是如何创建和使用Texture对象的。
创建Texture
以创建2D Texture为例。
uint CreateTexture(Bitmap bitmap)
{
glActiveTexture(OpenGL.GL_TEXTURE0);
var id = new uint[];
OpenGL.GenTextures(, id);
OpenGL.BindTexture(target, id[]);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_R, (int)OpenGL.GL_CLAMP_TO_EDGE);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_S, (int)OpenGL.GL_CLAMP_TO_EDGE);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_T, (int)OpenGL.GL_CLAMP_TO_EDGE);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, (int)OpenGL.GL_REPEAT);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, (int)OpenGL.GL_REPEAT); BitmapData bitmapData = bitmap.LockBits(new Rectangle(, , bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
OpenGL.TexImage2D(OpenGL.GL_TEXTURE_2D, , OpenGL.GL_RGBA, bitmap.Width, bitmap.Height, , OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, bitmapData.Scan0);
bitmap.UnlockBits(bitmapData); return id[];
}
CreateTexture
使用Texture
使用上述Texture的方式:
void UseTexture(string textureNameInShader, uint textureId)
{
uint target = OpenGL.GL_TEXTURE0;
glActiveTexture(target);
OpenGL.BindTexture(OpenGL.GL_TEXTURE_2D, textureId);
SetUniform("textureNameInShader", target - OpenGL.GL_TEXTURE0);
} int SetUniform(string uniformName, uint v0)
{
int location = GetUniformLocation(uniformName);
if (location >= )
{
glUniform1ui(GetUniformLocation(uniformName), v0);
}
return location;
}
封装的Texture
从上述创建Texture的过程可知,创建Texture主要有2个步骤:设置Sampler和填充Texture数据。Sampler就是各个滤波选项。填充数据就是用glTexImage2D()一类的命令指定Texture的内容。
void Initialize()
{
glActiveTexture(this.ActiveTexture);
OpenGL.GenTextures(, id);
BindTextureTarget target = this.Target;
OpenGL.BindTexture(target, id[]);
this.Sampler.Bind(this.ActiveTexture - OpenGL.GL_TEXTURE0, target);
this.ImageFiller.Fill(target);
OpenGL.GenerateMipmap((MipmapTarget)((uint)target));// TODO: does this work?
//this.SamplerBuilder.Unbind(OpenGL.GL_TEXTURE0 - OpenGL.GL_TEXTURE0, this.Target);
OpenGL.BindTexture(this.Target, );
}
Sampler
Sampler中主要就是那几个滤波选项。
/// <summary>
/// texture's settings.
/// </summary>
public class SamplerParameters
{
public TextureWrapping wrapS = TextureWrapping.ClampToEdge;
public TextureWrapping wrapT = TextureWrapping.ClampToEdge;
public TextureWrapping wrapR = TextureWrapping.ClampToEdge;
public TextureFilter minFilter = TextureFilter.Linear;
public TextureFilter magFilter = TextureFilter.Linear; public SamplerParameters() { }
}
Sampler的唯一任务就是在创建Texture时指定某些滤波。
/// <summary>
/// texture's settings.
/// </summary>
public abstract class SamplerBase
{
protected MipmapFilter mipmapFilter;
public SamplerParameters Parameters { get; protected set; } /// <summary>
/// texture's settings.
/// </summary>
/// <param name="parameters"></param>
/// <param name="mipmapFilter"></param>
public SamplerBase(SamplerParameters parameters, MipmapFilter mipmapFilter)
{
if (parameters == null)
{
this.Parameters = new SamplerParameters();
}
else
{
this.Parameters = parameters;
} this.mipmapFilter = mipmapFilter;
} /// <summary>
///
/// </summary>
/// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
/// <param name="target"></param>
public abstract void Bind(uint unit, BindTextureTarget target); }
实际上为了简化指定Sampler的操作,OpenGL提供了一个Sampler对象。这里顺便也把它封装了。
/// <summary>
/// texture's settings.
/// </summary>
public partial class Sampler : SamplerBase, IDisposable
{
/// <summary>
/// sampler's Id.
/// </summary>
public uint Id { get; private set; } /// <summary>
/// texture's settings.
/// </summary>
/// <param name="parameters"></param>
/// <param name="mipmapFiltering"></param>
public Sampler(
SamplerParameters parameters = null,
MipmapFilter mipmapFiltering = MipmapFilter.LinearMipmapLinear)
: base(parameters, mipmapFiltering)
{ } private bool initialized = false;
/// <summary>
///
/// </summary>
public void Initialize(uint unit, BindTextureTarget target)
{
if (!this.initialized)
{
this.DoInitialize(unit, target);
this.initialized = true;
}
} private void DoInitialize(uint unit, BindTextureTarget target)
{
var ids = new uint[];
OpenGL.GenSamplers(, ids);
this.Id = ids[];
//OpenGL.BindSampler(unit, ids[0]);
OpenGL.BindSampler(unit, ids[]);
/* Clamping to edges is important to prevent artifacts when scaling */
OpenGL.SamplerParameteri(ids[], OpenGL.GL_TEXTURE_WRAP_R, (int)this.parameters.wrapR);
OpenGL.SamplerParameteri(ids[], OpenGL.GL_TEXTURE_WRAP_S, (int)this.parameters.wrapS);
OpenGL.SamplerParameteri(ids[], OpenGL.GL_TEXTURE_WRAP_T, (int)this.parameters.wrapT);
/* Linear filtering usually looks best for text */
OpenGL.SamplerParameteri(ids[], OpenGL.GL_TEXTURE_MIN_FILTER, (int)this.parameters.minFilter);
OpenGL.SamplerParameteri(ids[], OpenGL.GL_TEXTURE_MAG_FILTER, (int)this.parameters.magFilter);
// TODO: mipmap not used yet. OpenGL.BindSampler(unit, );
}
/// <summary>
/// texture's settings.
/// </summary>
/// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
/// <param name="target"></param>
public override void Bind(uint unit, BindTextureTarget target)
{
if (!this.initialized) { this.Initialize(unit, target); } OpenGL.BindSampler(unit, this.Id);
}
}
Sampler
当然也可以不用这个OpenGL的Sampler对象,直接用glTexParameteri()等指令。这就像是一个假的Sampler对象在工作。
/// <summary>
/// texture's settings.
/// </summary>
public class FakeSampler : SamplerBase
{ /// <summary>
/// texture's settings.
/// </summary>
/// <param name="parameters"></param>
/// <param name="mipmapFiltering"></param>
public FakeSampler(SamplerParameters parameters, MipmapFilter mipmapFiltering)
: base(parameters, mipmapFiltering)
{
} /// <summary>
/// texture's settings.
/// </summary>
/// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
/// <param name="target"></param>
public override void Bind(uint unit, BindTextureTarget target)
{
/* Clamping to edges is important to prevent artifacts when scaling */
OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_WRAP_R, (int)this.parameters.wrapR);
OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_WRAP_S, (int)this.parameters.wrapS);
OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_WRAP_T, (int)this.parameters.wrapT);
/* Linear filtering usually looks best for text */
OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_MIN_FILTER, (int)this.parameters.minFilter);
OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_MAG_FILTER, (int)this.parameters.magFilter);
// TODO: mipmap filter not working yet. }
}
FakeSampler
当然,有的时候根本不需要指定任何滤波选项。这可以用一个空的Sampler类型实现。
/// <summary>
/// do nothing about sampling in building texture.
/// </summary>
public class NullSampler : SamplerBase
{
/// <summary>
/// do nothing about sampling in building texture.
/// </summary>
public NullSampler() : base(null, MipmapFilter.LinearMipmapLinear) { } /// <summary>
/// do nothing.
/// </summary>
/// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
/// <param name="target"></param>
public override void Bind(uint unit, BindTextureTarget target)
{
// nothing to do.
}
}
ImageFiller
填充数据就是用 glTexImage2D() 、 glTexStorage2D() 等指令设置Texture的内容。ImageFiller就是封装这一操作的。
/// <summary>
/// build texture's content.
/// </summary>
public abstract class ImageFiller
{ /// <summary>
/// build texture's content.
/// </summary>
/// <param name="target"></param>
public abstract void Fill(BindTextureTarget target);
}
对于常见的以 System.Drawing.Bitmap 为数据源填充Texture的情形,可以用下面的BitmapFiller。它可以作为1D/2D的Texture对象的填充器。
/// <summary>
/// build texture's content with Bitmap.
/// </summary>
public class BitmapFiller : ImageFiller
{
private System.Drawing.Bitmap bitmap;
private int level;
private uint internalformat;
private int border;
private uint format;
private uint type; /// <summary>
/// build texture's content with Bitmap.
/// </summary>
/// <param name="bitmap"></param>
/// <param name="level"></param>
/// <param name="internalformat">OpenGL.GL_RGBA etc.</param>
/// <param name="border"></param>
/// <param name="format">OpenGL.GL_BGRA etc.</param>
/// <param name="type">OpenGL.GL_UNSIGNED_BYTE etc.</param>
public BitmapFiller(System.Drawing.Bitmap bitmap,
int level, uint internalformat, int border, uint format, uint type)
{
this.bitmap = bitmap;
this.level = level;
this.internalformat = internalformat;
this.border = border;
this.format = format;
this.type = type;
} /// <summary>
/// build texture's content with Bitmap.
/// </summary>
/// <param name="target"></param>
public override void Fill(BindTextureTarget target)
{
// generate texture.
// Lock the image bits (so that we can pass them to OGL).
BitmapData bitmapData = bitmap.LockBits(new Rectangle(, , bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
if (target == BindTextureTarget.Texture1D)
{
OpenGL.TexImage1D((uint)target, , this.internalformat, bitmap.Width, , this.format, this.type, bitmapData.Scan0);
}
else if (target == BindTextureTarget.Texture2D)
{
OpenGL.TexImage2D((uint)target, , this.internalformat, bitmap.Width, bitmap.Height, , this.format, this.type, bitmapData.Scan0);
}
else
{ throw new NotImplementedException(); } // Unlock the image.
bitmap.UnlockBits(bitmapData);
}
}
BitmapFiller
还有一个常见的填充方式 glTexStorage2D() ,可以用下面的TexStorageImageFiller实现。
/// <summary>
///
/// </summary>
public class TexStorageImageFiller : ImageFiller
{
private int levels;
private uint internalFormat;
private int width;
private int height; /// <summary>
///
/// </summary>
/// <param name="levels"></param>
/// <param name="internalFormat"></param>
/// <param name="width"></param>
/// <param name="height"></param>
public TexStorageImageFiller(int levels, uint internalFormat, int width, int height)
{
// TODO: Complete member initialization
this.levels = levels;
this.internalFormat = internalFormat;
this.width = width;
this.height = height;
} /// <summary>
///
/// </summary>
/// <param name="target"></param>
public override void Fill(BindTextureTarget target)
{
switch (target)
{
case BindTextureTarget.Unknown:
break;
case BindTextureTarget.Texture1D:
break;
case BindTextureTarget.Texture2D:
OpenGL.TexStorage2D(TexStorage2DTarget.Texture2D, levels, internalFormat, width, height);
break;
case BindTextureTarget.Texture3D:
break;
case BindTextureTarget.TextureCubeMap:
break;
case BindTextureTarget.TextureBuffer:
break;
default:
break;
}
}
}
TexStorageImageFiller
创建Texture
用封装的类型创建Texture的方式如下:
Texture Create(Bitmap bitmap)
{
var texture = new Texture(BindTextureTarget.Texture2D,
new BitmapFiller(bitmap, , OpenGL.GL_RGBA32F, , OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE),
new SamplerParameters(
TextureWrapping.ClampToEdge,
TextureWrapping.ClampToEdge,
TextureWrapping.ClampToEdge,
TextureFilter.Linear,
TextureFilter.Linear));
texture.Initialize(); return texture;
}
使用Texture
Texutre.Id就是用 glGenTextures() 获得的id。Texture中记录了此Texture的ActiveTexture、Target等属性。配合CSharpGL中的 samplerValue ,我们有:
/// <summary>
/// get <see cref="samplerValue"/> from this texture.
/// </summary>
/// <param name="texture"></param>
/// <returns></returns>
public static samplerValue ToSamplerValue(this Texture texture)
{
return new samplerValue(
texture.Target,
texture.Id,
texture.ActiveTexture);
}
这就可以用到设置shader中需要的Texture上:
this.SetUniform("tex", texture.ToSamplerValue());
封装Framebuffer
过程式的Framebuffer
首先观察一下平时是如何创建和使用Framebuffer对象的。
创建Framebuffer
为关注重点,这里直接传入Texture的Id。
uint Create(int width, int height, uint textureId)
{
// create framebuffer.
var frameBufferId = new uint[];
glGenFramebuffers(, frameBufferId);
glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, frameBufferId); // attach texture as a color buffer.
glFramebufferTexture2D(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_COLOR_ATTACHMENT0, OpenGL.GL_TEXTURE_2D, textureId, ); // create a depth buffer.
var renderbufferId = new uint[];
glGenRenderbuffers(, renderbufferId);
glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, renderbufferId[]);
glRenderbufferStorage(OpenGL.GL_RENDERBUFFER, OpenGL.GL_DEPTH_COMPONENT, width, height); // attach depth buffer.
glFramebufferRenderbuffer(OpenGL.GL_RENDERBUFFER, OpenGL.GL_DEPTH_ATTACHMENT, OpenGL.GL_RENDERBUFFER, renderbufferId); glBindFramebuffer(OpenGL.GL_RENDERBUFFER, ); return frameBufferId;
}
使用Framebuffer
使用方式与Texture类似,只要绑定就可以了。
glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, frameBufferId);
用完再解绑。
glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, );
封装的Framebuffer
Framebuffer就是一个盒子,单独创建一个Framebuffer基本上是没什么用的。必须Attach一些colorbuffer/depthbuffer/texture才能发挥作用。
一个Framebuffer能够绑定多个texture和colorbuffer,只能绑定一个depthbuffer。
Renderbuffer
colorbuffer和depthbuffer都属于Renderbuffer的一种,其创建方式相同,只不过有一个标识其为colorbuffer还是depthbuffer的标志不同。
创建Renderbuffer很简单。
/// <summary>
/// Create, update, use and delete a renderbuffer object.
/// </summary>
public partial class Renderbuffer
{
uint[] renderbuffer = new uint[];
/// <summary>
/// Framebuffer Id.
/// </summary>
public uint Id { get { return renderbuffer[]; } } /// <summary>
/// Create, update, use and delete a renderbuffer object.
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="internalformat">GL_DEPTH_COMPONENT, GL_RGBA etc.</param>
/// <param name="bufferType"></param>
public Renderbuffer(int width, int height, uint internalformat, RenderbufferType bufferType)
{
this.Width = width;
this.Height = height;
this.BufferType = bufferType; glGenRenderbuffers(, renderbuffer);
glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, renderbuffer[]);
glRenderbufferStorage(OpenGL.GL_RENDERBUFFER,
internalformat, width, height);
} public int Width { get; set; }
public int Height { get; set; }
public RenderbufferType BufferType { get; private set; }
} public enum RenderbufferType
{
DepthBuffer,
ColorBuffer,
}
创建Framebuffer
创建Framebuffer也很简单,实际上只是调用了一个 glGenFramebuffers(, frameBuffer); 命令。
/// <summary>
/// Create, update, use and delete a framebuffer object.
/// </summary>
public partial class Framebuffer : IDisposable
{
uint[] frameBuffer = new uint[];
/// <summary>
/// Framebuffer Id.
/// </summary>
public uint Id { get { return frameBuffer[]; } } /// <summary>
/// Create an empty framebuffer object.
/// </summary>
public Framebuffer()
{
glGenFramebuffers(, frameBuffer);
}
} /// <summary>
///
/// </summary>
public enum FramebufferTarget : uint
{
/// <summary>
/// used to draw(write only) something.
/// </summary>
DrawFramebuffer = OpenGL.GL_DRAW_FRAMEBUFFER,
/// <summary>
/// used to read from(read only).
/// </summary>
ReadFramebuffer = OpenGL.GL_READ_FRAMEBUFFER,
/// <summary>
/// both read/write.
/// </summary>
Framebuffer = OpenGL.GL_FRAMEBUFFER,
}
使用Framebuffer
使用方式与Texture类似,只要绑定就可以了。
framebuffer.Bind();// glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebufferId);
用完再解绑。
framebuffer.Unbind();// glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0);
这与未封装的使用方式没什么区别。
总结
基于目前我对Texture和Framebuffer的了解,现在只能封装到这个地步。
CSharpGL(29)初步封装Texture和Framebuffer的更多相关文章
- httpclient初步封装
Http通信方式:HttpURLConnection和HttpClient HttpURLConnection是java的标准类,什么都没封装,用起来太原始,不方便HttpClient就是一个增强版的 ...
- 接口自动化:HttpClient + TestNG + Java(三) - 初步封装和testng断言
在上一篇中,我们写了第一个get请求的测试类,这一篇我们来对他进行初步优化和封装 3.1 分离请求发送类 首先想到的问题是,以后我们的接口自动化测试框架会大量用到发送http请求的功能. 那么这一部分 ...
- selenium2.0的初步封装(java版本)
我们都知道, 在本地创建java项目后,引入selenium-java-2.35.0.jar selenium-support-2.35.0.jar junit-4.8.1.jar等等jar包之后 ...
- 【NET】Winform用户控件的初步封装之编辑控件
编辑控件 public abstract partial class TEditorBase <TEntity, TRepository, TSqlStrConstruct> : User ...
- AFN的初步封装(post、GET、有无参数)
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface MyURLPost : NSObjec ...
- 小记:对Android网络下载工具的初步封装!(包括json,字符串下载(volley),和图片下载(glide))
import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkIn ...
- 【NET】Winform用户控件的初步封装之列表页控件
public abstract partial class TListPager<TEntity, TRepository, TSqlStrConstruct> : UserControl ...
- BIT祝威博客汇总(Blog Index)
+BIT祝威+悄悄在此留下版了个权的信息说: 关于硬件(Hardware) <穿越计算机的迷雾>笔记 继电器是如何成为CPU的(1) 继电器是如何成为CPU的(2) 关于操作系统(Oper ...
- CSharpGL(1)从最简单的例子开始使用CSharpGL
CSharpGL(1)从最简单的例子开始使用CSharpGL 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo ...
随机推荐
- Linux 内核概述 - Linux Kernel
Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...
- NodeJs之OS
OS Node.js提供了一些基本的底层操作系统的模块OS. API var os = require('os'); console.log('[arch] 操作系统CPU架构'+os.arch()) ...
- Restful资源文章
理解RESTful架构 RESTful API设计指南 RESTful架构详解 NodeJs的RESTful API
- .NET Core 系列5 :使用 Nuget打包类库
NuGet是个开源项目,项目包括 NuGet VS插件/NuGet Explorer/NuGetServer/NuGet命令行等项目,.NET Core项目完全使用Nuget 管理组件之间的依赖关系, ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- iOS开源项目周报0105
由OpenDigg 出品的iOS开源项目周报第四期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开发方面的开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. He ...
- git 命令
切换仓库地址: git remote set-url origin xxx.git切换分支:git checkout name撤销修改:git checkout -- file删除文件:git rm ...
- webpack+react+redux+es6开发模式
一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...
- C++的内存泄漏检测
C++大量的手动分配.回收内存是存在风险的,也许一个函数中一小块内存泄漏被重复放大之后,最后掏空内存. 这里介绍一种在debug模式下测试内存泄漏的方法. 首先在文件的开头以确定的顺序写下这段代码: ...
- 算法与数据结构(十五) 归并排序(Swift 3.0版)
上篇博客我们主要聊了堆排序的相关内容,本篇博客,我们就来聊一下归并排序的相关内容.归并排序主要用了分治法的思想,在归并排序中,将我们需要排序的数组进行拆分,将其拆分的足够小.当拆分的数组中只有一个元素 ...