生成一个立方体贴图(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,从而生成立方体贴图。

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

相关推荐
Haohao+++1 小时前
Stable Diffusion原理解析
人工智能·深度学习·算法
ideaout技术团队4 小时前
leetcode学习笔记2:多数元素(摩尔投票算法)
学习·算法·leetcode
代码充电宝4 小时前
LeetCode 算法题【简单】283. 移动零
java·算法·leetcode·职场和发展
不枯石6 小时前
Matlab通过GUI实现点云的均值滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab·均值算法
不枯石7 小时前
Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
白水先森8 小时前
C语言作用域与数组详解
java·数据结构·算法
想唱rap9 小时前
直接选择排序、堆排序、冒泡排序
c语言·数据结构·笔记·算法·新浪微博
老葱头蒸鸡10 小时前
(27)APS.NET Core8.0 堆栈原理通俗理解
算法
视睿10 小时前
【C++练习】06.输出100以内的所有素数
开发语言·c++·算法·机器人·无人机
柠檬071111 小时前
matlab cell 数据转换及记录
算法