• 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>
    28         /// 
    29         /// </summary>
    30         /// <param name="target"></param>
    31         public override void Fill(BindTextureTarget target)
    32         {
    33             switch (target)
    34             {
    35                 case BindTextureTarget.Unknown:
    36                     break;
    37                 case BindTextureTarget.Texture1D:
    38                     break;
    39                 case BindTextureTarget.Texture2D:
    40                     OpenGL.TexStorage2D(TexStorage2DTarget.Texture2D, levels, internalFormat, width, height);
    41                     break;
    42                 case BindTextureTarget.Texture3D:
    43                     break;
    44                 case BindTextureTarget.TextureCubeMap:
    45                     break;
    46                 case BindTextureTarget.TextureBuffer:
    47                     break;
    48                 default:
    49                     break;
    50             }
    51         }
    52     }
    TexStorageImageFiller
    +BIT祝威+悄悄在此留下版了个权的信息说:

    创建Texture

    用封装的类型创建Texture的方式如下:

     1 Texture Create(Bitmap bitmap)
     2 {
     3     var texture = new Texture(BindTextureTarget.Texture2D,
     4         new BitmapFiller(bitmap, 0, OpenGL.GL_RGBA32F, 0, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE),
     5         new SamplerParameters(
     6             TextureWrapping.ClampToEdge,
     7             TextureWrapping.ClampToEdge,
     8             TextureWrapping.ClampToEdge,
     9             TextureFilter.Linear,
    10             TextureFilter.Linear));
    11     texture.Initialize();
    12     
    13     return texture;
    14 }

    使用Texture

    Texutre.Id就是用 glGenTextures() 获得的id。Texture中记录了此Texture的ActiveTexture、Target等属性。配合CSharpGL中的 samplerValue ,我们有:

     1         /// <summary>
     2         /// get <see cref="samplerValue"/> from this texture.
     3         /// </summary>
     4         /// <param name="texture"></param>
     5         /// <returns></returns>
     6         public static samplerValue ToSamplerValue(this Texture texture)
     7         {
     8             return new samplerValue(
     9                 texture.Target,
    10                 texture.Id,
    11                 texture.ActiveTexture);
    12         }

    这就可以用到设置shader中需要的Texture上:

    this.SetUniform("tex", texture.ToSamplerValue());

    封装Framebuffer

    过程式的Framebuffer

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

    创建Framebuffer

    为关注重点,这里直接传入Texture的Id。

     1 uint Create(int width, int height, uint textureId)
     2 {
     3     // create framebuffer.
     4     var frameBufferId = new uint[1];
     5     glGenFramebuffers(1, frameBufferId);
     6     glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, frameBufferId);
     7     
     8     // attach texture as a color buffer.
     9     glFramebufferTexture2D(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_COLOR_ATTACHMENT0, OpenGL.GL_TEXTURE_2D, textureId, 0);
    10     
    11     // create a depth buffer.
    12     var renderbufferId = new uint[1];
    13     glGenRenderbuffers(1, renderbufferId);
    14     glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, renderbufferId[0]);
    15     glRenderbufferStorage(OpenGL.GL_RENDERBUFFER, OpenGL.GL_DEPTH_COMPONENT, width, height);
    16 
    17     // attach depth buffer.
    18     glFramebufferRenderbuffer(OpenGL.GL_RENDERBUFFER, OpenGL.GL_DEPTH_ATTACHMENT, OpenGL.GL_RENDERBUFFER, renderbufferId);
    19     
    20     glBindFramebuffer(OpenGL.GL_RENDERBUFFER, 0);
    21     
    22     return frameBufferId;
    23 }

    使用Framebuffer

    使用方式与Texture类似,只要绑定就可以了。

        glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, frameBufferId);

    用完再解绑。

        glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0);

    封装的Framebuffer

    Framebuffer就是一个盒子,单独创建一个Framebuffer基本上是没什么用的。必须Attach一些colorbuffer/depthbuffer/texture才能发挥作用。

    一个Framebuffer能够绑定多个texture和colorbuffer,只能绑定一个depthbuffer。

    Renderbuffer

    colorbuffer和depthbuffer都属于Renderbuffer的一种,其创建方式相同,只不过有一个标识其为colorbuffer还是depthbuffer的标志不同。

    创建Renderbuffer很简单。

     1     /// <summary>
     2     /// Create, update, use and delete a renderbuffer object.
     3     /// </summary>
     4     public partial class Renderbuffer
     5     {
     6         uint[] renderbuffer = new uint[1];
     7         /// <summary>
     8         /// Framebuffer Id.
     9         /// </summary>
    10         public uint Id { get { return renderbuffer[0]; } }
    11 
    12         /// <summary>
    13         /// Create, update, use and delete a renderbuffer object.
    14         /// </summary>
    15         /// <param name="width"></param>
    16         /// <param name="height"></param>
    17         /// <param name="internalformat">GL_DEPTH_COMPONENT, GL_RGBA etc.</param>
    18         /// <param name="bufferType"></param>
    19         public Renderbuffer(int width, int height, uint internalformat, RenderbufferType bufferType)
    20         {
    21             this.Width = width;
    22             this.Height = height;
    23             this.BufferType = bufferType;
    24 
    25             glGenRenderbuffers(1, renderbuffer);
    26             glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, renderbuffer[0]);
    27             glRenderbufferStorage(OpenGL.GL_RENDERBUFFER,
    28                 internalformat, width, height);
    29         }
    30 
    31         public int Width { get; set; }
    32         public int Height { get; set; }
    33         public RenderbufferType BufferType { get; private set; }
    34     }
    35 
    36     public enum RenderbufferType
    37     {
    38         DepthBuffer,
    39         ColorBuffer,
    40     }
    +BIT祝威+悄悄在此留下版了个权的信息说:

    创建Framebuffer

    创建Framebuffer也很简单,实际上只是调用了一个 glGenFramebuffers(1, frameBuffer); 命令。

     1     /// <summary>
     2     /// Create, update, use and delete a framebuffer object.
     3     /// </summary>
     4     public partial class Framebuffer : IDisposable
     5     {
     6         uint[] frameBuffer = new uint[1];
     7         /// <summary>
     8         /// Framebuffer Id.
     9         /// </summary>
    10         public uint Id { get { return frameBuffer[0]; } }
    11 
    12         /// <summary>
    13         /// Create an empty framebuffer object.
    14         /// </summary>
    15         public Framebuffer()
    16         {
    17             glGenFramebuffers(1, frameBuffer);
    18         }
    19     }
    20 
    21     /// <summary>
    22     /// 
    23     /// </summary>
    24     public enum FramebufferTarget : uint
    25     {
    26         /// <summary>
    27         /// used to draw(write only) something.
    28         /// </summary>
    29         DrawFramebuffer = OpenGL.GL_DRAW_FRAMEBUFFER,
    30         /// <summary>
    31         /// used to read from(read only).
    32         /// </summary>
    33         ReadFramebuffer = OpenGL.GL_READ_FRAMEBUFFER,
    34         /// <summary>
    35         /// both read/write.
    36         /// </summary>
    37         Framebuffer = OpenGL.GL_FRAMEBUFFER,
    38     }

    使用Framebuffer

    使用方式与Texture类似,只要绑定就可以了。

        framebuffer.Bind();// glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebufferId);

    用完再解绑。

        framebuffer.Unbind();// glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0);

    这与未封装的使用方式没什么区别。

    总结

    基于目前我对Texture和Framebuffer的了解,现在只能封装到这个地步。

    +BIT祝威+悄悄在此留下版了个权的信息说:
  • 相关阅读:
    2018-2019-2 实验三 敏捷开发与XP实践
    计算机网络课外实验一级 20175319江野
    2018-2019-2 《Java程序设计》第9周学习总结
    MyCP(课下作业,必做)
    [NOIP2012] 疫情控制
    [SPOJ2021] Moving Pebbles
    谁能赢呢?
    [HEOI2014] 人人尽说江南好
    [笔记] 巴什博弈
    [SCOI2008] 着色方案
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-29-wrapping-Texture-and-Framebuffer.html
Copyright © 2020-2023  润新知