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

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

相关推荐
Inverse16220 分钟前
C语言_动态内存管理
c语言·数据结构·算法
数据与人工智能律师22 分钟前
虚拟主播肖像权保护,数字时代的法律博弈
大数据·网络·人工智能·算法·区块链
wuqingshun3141591 小时前
蓝桥杯 16. 外卖店优先级
c++·算法·职场和发展·蓝桥杯·深度优先
YouQian7722 小时前
2025春训第十九场
算法
CodeJourney.2 小时前
基于MATLAB的生物量数据拟合模型研究
人工智能·爬虫·算法·matlab·信息可视化
Epiphany.5562 小时前
素数筛(欧拉筛算法)
c++·算法·图论
爱吃涮毛肚的肥肥(暂时吃不了版)2 小时前
项目班——0510——JSON网络封装
c++·算法·json
liang_20262 小时前
【HT周赛】T3.二维平面 题解(分块:矩形chkmax,求矩形和)
数据结构·笔记·学习·算法·平面·总结
緈福的街口2 小时前
【leetcode】2900. 最长相邻不相等子序列 I
算法·leetcode·职场和发展
易只轻松熊2 小时前
C++(20): 文件输入输出库 —— <fstream>
开发语言·c++·算法