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 中生成立方体贴图。
代码解析:
变量定义和初始化:
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 面计算:
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 面类似,只是方向相反。代码如下:
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 面计算:
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 设置为负值。代码如下:
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 面:
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 面:
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,从而生成立方体贴图。
代码结构中有很多重复的部分,可以考虑通过提取成函数来进行重构,从而减少冗余。