CSharpGL(29)初步封装Texture和Framebuffer

Posted 天下事有难易乎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSharpGL(29)初步封装Texture和Framebuffer相关的知识,希望对你有一定的参考价值。

+BIT祝威+悄悄在此留下版了个权的信息说:

 

CSharpGL(29)初步封装Texture和Framebuffer

+BIT祝威+悄悄在此留下版了个权的信息说:

Texture和Framebuffer

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

volume rendering

+BIT祝威+悄悄在此留下版了个权的信息说:

下载

CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

+BIT祝威+悄悄在此留下版了个权的信息说:

封装Texture

过程式的Texture

首先观察一下平时是如何创建和使用Texture对象的。

+BIT祝威+悄悄在此留下版了个权的信息说:

创建Texture

以创建2D Texture为例。

 1 uint CreateTexture(Bitmap bitmap)
 2 {
 3     glActiveTexture(OpenGL.GL_TEXTURE0);
 4     var id = new uint[1];
 5     OpenGL.GenTextures(1, id);
 6     OpenGL.BindTexture(target, id[0]);
 7     OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_R, (int)OpenGL.GL_CLAMP_TO_EDGE);
 8     OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_S, (int)OpenGL.GL_CLAMP_TO_EDGE);
 9     OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_T, (int)OpenGL.GL_CLAMP_TO_EDGE);
10     OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, (int)OpenGL.GL_REPEAT);
11     OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, (int)OpenGL.GL_REPEAT);
12     
13     BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
14         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
15     OpenGL.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_RGBA, bitmap.Width, bitmap.Height, 0, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, bitmapData.Scan0);
16     bitmap.UnlockBits(bitmapData);
17     
18     return id[0];
19 }
CreateTexture 

使用Texture

使用上述Texture的方式:

 1 void UseTexture(string textureNameInShader, uint textureId)
 2 {
 3     uint target = OpenGL.GL_TEXTURE0;
 4     glActiveTexture(target);
 5     OpenGL.BindTexture(OpenGL.GL_TEXTURE_2D, textureId);
 6     SetUniform("textureNameInShader", target - OpenGL.GL_TEXTURE0);
 7 }
 8 
 9 int SetUniform(string uniformName, uint v0)
10 {
11     int location = GetUniformLocation(uniformName);
12     if (location >= 0)
13     {
14         glUniform1ui(GetUniformLocation(uniformName), v0);
15     }
16     return location;
17 }

封装的Texture

从上述创建Texture的过程可知,创建Texture主要有2个步骤:设置Sampler填充Texture数据。Sampler就是各个滤波选项。填充数据就是用glTexImage2D()一类的命令指定Texture的内容。

 1 void Initialize()
 2 {
 3     glActiveTexture(this.ActiveTexture);
 4     OpenGL.GenTextures(1, id);
 5     BindTextureTarget target = this.Target;
 6     OpenGL.BindTexture(target, id[0]);
 7     this.Sampler.Bind(this.ActiveTexture - OpenGL.GL_TEXTURE0, target);
 8     this.ImageFiller.Fill(target);
 9     OpenGL.GenerateMipmap((MipmapTarget)((uint)target));// TODO: does this work?
10     //this.SamplerBuilder.Unbind(OpenGL.GL_TEXTURE0 - OpenGL.GL_TEXTURE0, this.Target);
11     OpenGL.BindTexture(this.Target, 0);
12 }
+BIT祝威+悄悄在此留下版了个权的信息说:

Sampler

Sampler中主要就是那几个滤波选项。

 1     /// <summary>
 2     /// texture\'s settings.
 3     /// </summary>
 4     public class SamplerParameters
 5     {
 6         public TextureWrapping wrapS = TextureWrapping.ClampToEdge;
 7         public TextureWrapping wrapT = TextureWrapping.ClampToEdge;
 8         public TextureWrapping wrapR = TextureWrapping.ClampToEdge;
 9         public TextureFilter minFilter = TextureFilter.Linear;
10         public TextureFilter magFilter = TextureFilter.Linear;
11 
12         public SamplerParameters() { }
13     }
+BIT祝威+悄悄在此留下版了个权的信息说:

Sampler的唯一任务就是在创建Texture时指定某些滤波。

 1     /// <summary>
 2     /// texture\'s settings.
 3     /// </summary>
 4     public abstract class SamplerBase
 5     {
 6         protected MipmapFilter mipmapFilter;
 7         public SamplerParameters Parameters { get; protected set; }
 8 
 9         /// <summary>
10         /// texture\'s settings.
11         /// </summary>
12         /// <param name="parameters"></param>
13         /// <param name="mipmapFilter"></param>
14         public SamplerBase(SamplerParameters parameters, MipmapFilter mipmapFilter)
15         {
16             if (parameters == null)
17             {
18                 this.Parameters = new SamplerParameters();
19             }
20             else
21             {
22                 this.Parameters = parameters;
23             }
24 
25             this.mipmapFilter = mipmapFilter;
26         }
27 
28         /// <summary>
29         /// 
30         /// </summary>
31         /// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
32         /// <param name="target"></param>
33         public abstract void Bind(uint unit, BindTextureTarget target);
34 
35     }

实际上为了简化指定Sampler的操作,OpenGL提供了一个Sampler对象。这里顺便也把它封装了。

 1     /// <summary>
 2     /// texture\'s settings.
 3     /// </summary>
 4     public partial class Sampler : SamplerBase, IDisposable
 5     {
 6         /// <summary>
 7         /// sampler\'s Id.
 8         /// </summary>
 9         public uint Id { get; private set; }
10 
11         /// <summary>
12         /// texture\'s settings.
13         /// </summary>
14         /// <param name="parameters"></param>
15         /// <param name="mipmapFiltering"></param>
16         public Sampler(
17             SamplerParameters parameters = null,
18             MipmapFilter mipmapFiltering = MipmapFilter.LinearMipmapLinear)
19             : base(parameters, mipmapFiltering)
20         {
21 
22         }
23 
24         private bool initialized = false;
25         /// <summary>
26         /// 
27         /// </summary>
28         public void Initialize(uint unit, BindTextureTarget target)
29         {
30             if (!this.initialized)
31             {
32                 this.DoInitialize(unit, target);
33                 this.initialized = true;
34             }
35         }
36 
37         private void DoInitialize(uint unit, BindTextureTarget target)
38         {
39             var ids = new uint[1];
40             OpenGL.GenSamplers(1, ids);
41             this.Id = ids[0];
42             //OpenGL.BindSampler(unit, ids[0]);
43             OpenGL.BindSampler(unit, ids[0]);
44             /* Clamping to edges is important to prevent artifacts when scaling */
45             OpenGL.SamplerParameteri(ids[0], OpenGL.GL_TEXTURE_WRAP_R, (int)this.parameters.wrapR);
46             OpenGL.SamplerParameteri(ids[0], OpenGL.GL_TEXTURE_WRAP_S, (int)this.parameters.wrapS);
47             OpenGL.SamplerParameteri(ids[0], OpenGL.GL_TEXTURE_WRAP_T, (int)this.parameters.wrapT);
48             /* Linear filtering usually looks best for text */
49             OpenGL.SamplerParameteri(ids[0], OpenGL.GL_TEXTURE_MIN_FILTER, (int)this.parameters.minFilter);
50             OpenGL.SamplerParameteri(ids[0], OpenGL.GL_TEXTURE_MAG_FILTER, (int)this.parameters.magFilter);
51             // TODO: mipmap not used yet.
52 
53             OpenGL.BindSampler(unit, 0);
54         }
55         /// <summary>
56         /// texture\'s settings.
57         /// </summary>
58         /// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
59         /// <param name="target"></param>
60         public override void Bind(uint unit, BindTextureTarget target)
61         {
62             if (!this.initialized) { this.Initialize(unit, target); }
63 
64             OpenGL.BindSampler(unit, this.Id);
65         }
66     }
Sampler
+BIT祝威+悄悄在此留下版了个权的信息说:

当然也可以不用这个OpenGL的Sampler对象,直接用glTexParameteri()等指令。这就像是一个假的Sampler对象在工作。

 1     /// <summary>
 2     /// texture\'s settings.
 3     /// </summary>
 4     public class FakeSampler : SamplerBase
 5     {
 6 
 7         /// <summary>
 8         /// texture\'s settings.
 9         /// </summary>
10         /// <param name="parameters"></param>
11         /// <param name="mipmapFiltering"></param>
12         public FakeSampler(SamplerParameters parameters, MipmapFilter mipmapFiltering)
13             : base(parameters, mipmapFiltering)
14         {
15         }
16 
17         /// <summary>
18         /// texture\'s settings.
19         /// </summary>
20         /// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
21         /// <param name="target"></param>
22         public override void Bind(uint unit, BindTextureTarget target)
23         {
24             /* Clamping to edges is important to prevent artifacts when scaling */
25             OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_WRAP_R, (int)this.parameters.wrapR);
26             OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_WRAP_S, (int)this.parameters.wrapS);
27             OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_WRAP_T, (int)this.parameters.wrapT);
28             /* Linear filtering usually looks best for text */
29             OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_MIN_FILTER, (int)this.parameters.minFilter);
30             OpenGL.TexParameteri((uint)target, OpenGL.GL_TEXTURE_MAG_FILTER, (int)this.parameters.magFilter);
31             // TODO: mipmap filter not working yet.
32 
33         }
34     }
FakeSampler

当然,有的时候根本不需要指定任何滤波选项。这可以用一个空的Sampler类型实现。

 1     /// <summary>
 2     /// do nothing about sampling in building texture.
 3     /// </summary>
 4     public class NullSampler : SamplerBase
 5     {
 6         /// <summary>
 7         /// do nothing about sampling in building texture.
 8         /// </summary>
 9         public NullSampler() : base(null, MipmapFilter.LinearMipmapLinear) { }
10 
11         /// <summary>
12         /// do nothing.
13         /// </summary>
14         /// <param name="unit">OpenGL.GL_TEXTURE0 etc.</param>
15         /// <param name="target"></param>
16         public override void Bind(uint unit, BindTextureTarget target)
17         {
18             // nothing to do.
19         }
20     }

 

+BIT祝威+悄悄在此留下版了个权的信息说:

ImageFiller

填充数据就是用 glTexImage2D() 、 glTexStorage2D() 等指令设置Texture的内容。ImageFiller就是封装这一操作的。

 1     /// <summary>
 2     /// build texture\'s content.
 3     /// </summary>
 4     public abstract class ImageFiller
 5     {
 6 
 7         /// <summary>
 8         /// build texture\'s content.
 9         /// </summary>
10         /// <param name="target"></param>
11         public abstract void Fill(BindTextureTarget target);
12     }

对于常见的以 System.Drawing.Bitmap 为数据源填充Texture的情形,可以用下面的BitmapFiller。它可以作为1D/2D的Texture对象的填充器。

 1     /// <summary>
 2     /// build texture\'s content with Bitmap.
 3     /// </summary>
 4     public class BitmapFiller : ImageFiller
 5     {
 6         private System.Drawing.Bitmap bitmap;
 7         private int level;
 8         private uint internalformat;
 9         private int border;
10         private uint format;
11         private uint type;
12 
13         /// <summary>
14         /// build texture\'s content with Bitmap.
15         /// </summary>
16         /// <param name="bitmap"></param>
17         /// <param name="level">0</param>
18         /// <param name="internalformat">OpenGL.GL_RGBA etc.</param>
19         /// <param name="border">0</param>
20         /// <param name="format">OpenGL.GL_BGRA etc.</param>
21         /// <param name="type">OpenGL.GL_UNSIGNED_BYTE etc.</param>
22         public BitmapFiller(System.Drawing.Bitmap bitmap,
23             int level, uint internalformat, int border, uint format, uint type)
24         {
25             this.bitmap = bitmap;
26             this.level = level;
27             this.internalformat = internalformat;
28             this.border = border;
29             this.format = format;
30             this.type = type;
31         }
32 
33         /// <summary>
34         /// build texture\'s content with Bitmap.
35         /// </summary>
36         /// <param name="target"></param>
37         public override void Fill(BindTextureTarget target)
38         {
39             // generate texture.
40             //  Lock the image bits (so that we can pass them to OGL).
41             BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
42                 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
43             if (target == BindTextureTarget.Texture1D)
44             {
45                 OpenGL.TexImage1D((uint)target, 0, this.internalformat, bitmap.Width, 0, this.format, this.type, bitmapData.Scan0);
46             }
47             else if (target == BindTextureTarget.Texture2D)
48             {
49                 OpenGL.TexImage2D((uint)target, 0, this.internalformat, bitmap.Width, bitmap.Height, 0, this.format, this.type, bitmapData.Scan0);
50             }
51             else
52             { throw new NotImplementedException(); }
53 
54             //  Unlock the image.
55             bitmap.UnlockBits(bitmapData);
56         }
57     }
BitmapFiller

还有一个常见的填充方式 glTexStorage2D() ,可以用下面的TexStorageImageFiller实现。

 1     /// <summary>
 2     /// 
 3     /// </summary>
 4     public class TexStorageImageFiller : ImageFiller
 5     {
 6         private int levels;
 7         private uint internalFormat;
 8         private int width;
 9         private int height;
10 
11         /// <summary>
12         /// 
13         /// </summary>
14         /// <param name="levels"></param>
15         /// <param name="internalFormat"></param>
16         /// <param name="width"></param>
17         /// <param name="height"></param>
18         public TexStorageImageFiller(int levels, uint internalFormat, int width, int height)
19         {
20             // TODO: Complete member initialization
21             this.levels = levels;
22             this.internalFormat = internalFormat;
23             this.width = width;
24             this.height = height;
25         }
26 
27         /// <summary>

以上是关于CSharpGL(29)初步封装Texture和Framebuffer的主要内容,如果未能解决你的问题,请参考以下文章

CSharpGL(56)[译]Vulkan入门(转)

CSharpGL(41)改进获取字形贴图的方法

CSharpGL(50)使用Assimp加载骨骼动画

CSharpGL(15)用GLSL渲染2种类型的文字

CSharpGL(31)[译]OpenGL渲染管道那些事

CSharpGL(26)在opengl中实现控件布局/渲染文字