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

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

相关推荐
I'm a winner38 分钟前
基于YOLO算法的医疗应用专题:第一章 计算机视觉与深度学习概述
算法·yolo·计算机视觉
vir021 小时前
P1928 外星密码(dfs)
java·数据结构·算法·深度优先·1024程序员节
喜欢吃燃面1 小时前
数据结构算法题:list
开发语言·c++·学习·算法·1024程序员节
寂静山林1 小时前
UVa 12991 Game Rooms
算法·1024程序员节
余俊晖2 小时前
RLVR训练多模态文档解析模型-olmOCR 2技术方案(模型、数据和代码均开源)
人工智能·算法·ocr·grpo
凉虾皮3 小时前
2024包河初中组
学习·算法·1024程序员节
m0_748233643 小时前
C++ 模板初阶:从函数重载到泛型编程的优雅过渡
java·c++·算法·1024程序员节
以己之4 小时前
11.盛最多水的容器
java·算法·双指针·1024程序员节
初级炼丹师(爱说实话版)4 小时前
算法面经常考题整理(3)大模型
算法
Neil今天也要学习5 小时前
永磁同步电机无速度算法--基于相位超前校正的LESO
算法·1024程序员节