生成一个立方体贴图(Cube Map)

cs 复制代码
private bool GenerateNormalisationCubeMap()
        {
            var gl = openGLControl1.OpenGL;

            //  First we create space to hold the data for a single face.
            //  Each face is 32x32, and we need to store the R, G and B components of the color at each point.
            byte[] data = new byte[32 * 32 * 3];

            //  Some useful variables.
            int size = 32;
            float offset = 0.5f;
            float halfSize = 16.0f;
            Vertex tempVector = new Vertex();
            uint byteCounter = 0;

            //  Positive x.
            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    tempVector.X = (halfSize);
                    tempVector.Y = (-(j + offset - halfSize));
                    tempVector.Z = (-(i + offset - halfSize));
                    tempVector.UnitLength();
                    tempVector = tempVector.GetPackedTo01();
                    data[byteCounter++] = (byte)(tempVector.X * 255f);
                    data[byteCounter++] = (byte)(tempVector.Y * 255f);
                    data[byteCounter++] = (byte)(tempVector.Z * 255f);
                }
            }

            //  Set the texture image.
            gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

            //negative x
            byteCounter = 0;

            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    tempVector.X = (-halfSize);
                    tempVector.Y = (-(j + offset - halfSize));
                    tempVector.Z = ((i + offset - halfSize));

                    tempVector.UnitLength();
                    tempVector = tempVector.GetPackedTo01();

                    data[byteCounter++] = (byte)(tempVector.X * 255f);
                    data[byteCounter++] = (byte)(tempVector.Y * 255f);
                    data[byteCounter++] = (byte)(tempVector.Z * 255f);
                }
            }
            gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                            0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

            //positive y
            byteCounter = 0;

            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    tempVector.X = (i + offset - halfSize);
                    tempVector.Y = (halfSize);
                    tempVector.Z = ((j + offset - halfSize));

                    tempVector.UnitLength();
                    tempVector = tempVector.GetPackedTo01();

                    data[byteCounter++] = (byte)(tempVector.X * 255f);
                    data[byteCounter++] = (byte)(tempVector.Y * 255f);
                    data[byteCounter++] = (byte)(tempVector.Z * 255f);
                }
            }
            gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                            0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

            //negative y
            byteCounter = 0;

            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    tempVector.X = (i + offset - halfSize);
                    tempVector.Y = (-halfSize);
                    tempVector.Z = (-(j + offset - halfSize));

                    tempVector.UnitLength();
                    tempVector = tempVector.GetPackedTo01();

                    data[byteCounter++] = (byte)(tempVector.X * 255f);
                    data[byteCounter++] = (byte)(tempVector.Y * 255f);
                    data[byteCounter++] = (byte)(tempVector.Z * 255f);

                }
            }
            gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                            0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

            //positive z
            byteCounter = 0;

            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    tempVector.X = (i + offset - halfSize);
                    tempVector.Y = (-(j + offset - halfSize));
                    tempVector.Z = (halfSize);

                    tempVector.UnitLength();
                    tempVector = tempVector.GetPackedTo01();

                    data[byteCounter++] = (byte)(tempVector.X * 255f);
                    data[byteCounter++] = (byte)(tempVector.Y * 255f);
                    data[byteCounter++] = (byte)(tempVector.Z * 255f);

                }
            }
            gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                            0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

            //negative z
            byteCounter = 0;

            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    tempVector.X = (-(i + offset - halfSize));
                    tempVector.Y = (-(j + offset - halfSize));
                    tempVector.Z = (-halfSize);

                    tempVector.UnitLength();
                    tempVector = tempVector.GetPackedTo01();

                    data[byteCounter++] = (byte)(tempVector.X * 255f);
                    data[byteCounter++] = (byte)(tempVector.Y * 255f);
                    data[byteCounter++] = (byte)(tempVector.Z * 255f);
                }
            }
            gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
                            0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

            return true;
        }

这段代码的主要目的是生成一个立方体贴图(Cube Map)。在 OpenGL 中,立方体贴图通常用于环境映射、天空盒(Skybox)等效果。每个面都会生成一个 32x32 的像素格子,并通过计算每个面上每个像素的位置来获得其颜色值。这里是通过计算每个像素对应的单位向量(normalization vector),然后将该向量转换为颜色数据(RGB),最终上传到 OpenGL 中生成立方体贴图。

代码解析:

变量定义和初始化:

cs 复制代码
byte[] data = new byte[32 * 32 * 3];
int size = 32;
float offset = 0.5f;
float halfSize = 16.0f;
Vertex tempVector = new Vertex();
uint byteCounter = 0;

data 是存储每个面数据的数组,大小为 32x32 的每个像素 3 个通道的 RGB 数据(即 32 * 32 * 3 字节)。

size 表示每个面是 32x32 的大小。

offset 和 halfSize 用于计算立方体的每个像素点的坐标。

tempVector 用于存储每个计算得到的顶点(向量)。

byteCounter 用来记录当前存储的位置。

正 X 面计算:

cs 复制代码
for (int j = 0; j < size; j++)
{
    for (int i = 0; i < size; i++)
    {
        tempVector.X = (halfSize);
        tempVector.Y = (-(j + offset - halfSize));
        tempVector.Z = (-(i + offset - halfSize));
        tempVector.UnitLength();
        tempVector = tempVector.GetPackedTo01();
        data[byteCounter++] = (byte)(tempVector.X * 255f);
        data[byteCounter++] = (byte)(tempVector.Y * 255f);
        data[byteCounter++] = (byte)(tempVector.Z * 255f);
    }
}
gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

这部分代码是用来计算并生成正 X 面的纹理数据。每个像素的位置会根据当前的 i 和 j 值来计算对应的三维坐标(tempVector.X, tempVector.Y, tempVector.Z)。

然后调用 UnitLength() 方法对向量进行归一化,确保每个向量的长度为 1。

接着调用 GetPackedTo01() 方法将向量的各分量映射到 0 到 1 之间。

最后,将计算出的 RGB 值存入 data 数组中,并通过 gl.TexImage2D 函数上传数据到 OpenGL 的正 X 面。

负 X 面计算: 负 X 面的计算与正 X 面类似,只是方向相反。代码如下:

cs 复制代码
byteCounter = 0;
for (int j = 0; j < size; j++)
{
    for (int i = 0; i < size; i++)
    {
        tempVector.X = (-halfSize);
        tempVector.Y = (-(j + offset - halfSize));
        tempVector.Z = ((i + offset - halfSize));
        tempVector.UnitLength();
        tempVector = tempVector.GetPackedTo01();
        data[byteCounter++] = (byte)(tempVector.X * 255f);
        data[byteCounter++] = (byte)(tempVector.Y * 255f);
        data[byteCounter++] = (byte)(tempVector.Z * 255f);
    }
}
gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

tempVector.X 设置为负值,其他计算过程与正 X 面相同。

正 Y 面计算:

cs 复制代码
byteCounter = 0;
for (int j = 0; j < size; j++)
{
    for (int i = 0; i < size; i++)
    {
        tempVector.X = (i + offset - halfSize);
        tempVector.Y = (halfSize);
        tempVector.Z = ((j + offset - halfSize));
        tempVector.UnitLength();
        tempVector = tempVector.GetPackedTo01();
        data[byteCounter++] = (byte)(tempVector.X * 255f);
        data[byteCounter++] = (byte)(tempVector.Y * 255f);
        data[byteCounter++] = (byte)(tempVector.Z * 255f);
    }
}
gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

这部分代码计算的是正 Y 面的数据,与正 X 面类似,不同的是 tempVector.Y 设置为正值,表示 Y 轴上的正方向。

负 Y 面计算: 负 Y 面的计算与正 Y 面类似,只是 tempVector.Y 设置为负值。代码如下:

cs 复制代码
byteCounter = 0;
for (int j = 0; j < size; j++)
{
    for (int i = 0; i < size; i++)
    {
        tempVector.X = (i + offset - halfSize);
        tempVector.Y = (-halfSize);
        tempVector.Z = (-(j + offset - halfSize));
        tempVector.UnitLength();
        tempVector = tempVector.GetPackedTo01();
        data[byteCounter++] = (byte)(tempVector.X * 255f);
        data[byteCounter++] = (byte)(tempVector.Y * 255f);
        data[byteCounter++] = (byte)(tempVector.Z * 255f);
    }
}
gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

正 Z 面和负 Z 面的计算: 正 Z 面和负 Z 面的计算方法与前面的类似,只是 tempVector.Z 设置为正值或负值。

正 Z 面:

cs 复制代码
byteCounter = 0;
for (int j = 0; j < size; j++)
{
    for (int i = 0; i < size; i++)
    {
        tempVector.X = (i + offset - halfSize);
        tempVector.Y = (-(j + offset - halfSize));
        tempVector.Z = (halfSize);
        tempVector.UnitLength();
        tempVector = tempVector.GetPackedTo01();
        data[byteCounter++] = (byte)(tempVector.X * 255f);
        data[byteCounter++] = (byte)(tempVector.Y * 255f);
        data[byteCounter++] = (byte)(tempVector.Z * 255f);
    }
}
gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

负 Z 面:

cs 复制代码
byteCounter = 0;
for (int j = 0; j < size; j++)
{
    for (int i = 0; i < size; i++)
    {
        tempVector.X = (-(i + offset - halfSize));
        tempVector.Y = (-(j + offset - halfSize));
        tempVector.Z = (-halfSize);
        tempVector.UnitLength();
        tempVector = tempVector.GetPackedTo01();
        data[byteCounter++] = (byte)(tempVector.X * 255f);
        data[byteCounter++] = (byte)(tempVector.Y * 255f);
        data[byteCounter++] = (byte)(tempVector.Z * 255f);
    }
}
gl.TexImage2D(OpenGL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
                0, OpenGL.GL_RGBA8, 32, 32, 0, OpenGL.GL_RGB, OpenGL.GL_UNSIGNED_BYTE, data);

总结:

这段代码通过计算每个面上每个像素对应的单位向量,将向量值转换为 RGB 颜色,并使用 OpenGL 的 TexImage2D 方法将数据上传到 GPU,从而生成立方体贴图。

代码结构中有很多重复的部分,可以考虑通过提取成函数来进行重构,从而减少冗余。

相关推荐
茉莉玫瑰花茶3 分钟前
C++ 17 详细特性解析(5)
开发语言·c++·算法
cpp_250115 分钟前
P10570 [JRKSJ R8] 网球
数据结构·c++·算法·题解
cpp_250122 分钟前
P8377 [PFOI Round1] 暴龙的火锅
数据结构·c++·算法·题解·洛谷
uesowys30 分钟前
Apache Spark算法开发指导-Factorization machines classifier
人工智能·算法
季明洵1 小时前
C语言实现单链表
c语言·开发语言·数据结构·算法·链表
shandianchengzi1 小时前
【小白向】错位排列|图文解释公考常见题目错位排列的递推式Dn=(n-1)(Dn-2+Dn-1)推导方式
笔记·算法·公考·递推·排列·考公
I_LPL1 小时前
day26 代码随想录算法训练营 回溯专题5
算法·回溯·hot100·求职面试·n皇后·解数独
Yeats_Liao1 小时前
评估体系构建:基于自动化指标与人工打分的双重验证
运维·人工智能·深度学习·算法·机器学习·自动化
cpp_25011 小时前
P9586 「MXOI Round 2」游戏
数据结构·c++·算法·题解·洛谷
浅念-1 小时前
C语言编译与链接全流程:从源码到可执行程序的幕后之旅
c语言·开发语言·数据结构·经验分享·笔记·学习·算法