返回
GLES2战记第五集——宇宙之光
Android
2023-12-09 08:02:47
前言
在上一篇中,我们学习了如何使用GLES2实现一个简单的球面。在本篇中,我们将在这个基础上,进一步实现一个宇宙之光的效果。
理论知识
宇宙之光的效果其实很简单,它就是通过在球面上绘制一层光晕来实现的。光晕的绘制可以通过使用一种叫做“纹理贴图”的技术来实现。纹理贴图可以将一张图片贴到三维物体上,从而使三维物体看起来更加真实。
在GLES2中,纹理贴图的实现非常简单,只需要使用glTexImage2D()
函数将图片数据上传到GPU,然后使用glBindTexture()
函数将图片绑定到三维物体上即可。
实现代码
下面是实现宇宙之光效果的代码:
// 顶点着色器代码
attribute vec3 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
uniform mat4 u_MVPMatrix;
void main() {
gl_Position = u_MVPMatrix * vec4(a_Position, 1.0);
v_TexCoord = a_TexCoord;
}
// 片元着色器代码
precision mediump float;
varying vec2 v_TexCoord;
uniform sampler2D u_Texture;
void main() {
gl_FragColor = texture2D(u_Texture, v_TexCoord);
}
// 主函数
int main() {
// 初始化EGL和GLES2环境
// ...
// 创建一个纹理对象
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
// 将图片数据上传到GPU
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 创建一个球面的顶点数据
float radius = 1.0f;
int numSlices = 32;
int numStacks = 32;
float[] vertices = new float[numSlices * numStacks * 6];
float[] texCoords = new float[numSlices * numStacks * 6];
int index = 0;
for (int i = 0; i < numSlices; i++) {
float theta = (float)i * 2.0f * Math.PI / numSlices;
float sinTheta = (float)Math.sin(theta);
float cosTheta = (float)Math.cos(theta);
for (int j = 0; j < numStacks; j++) {
float phi = (float)j * 2.0f * Math.PI / numStacks;
float sinPhi = (float)Math.sin(phi);
float cosPhi = (float)Math.cos(phi);
vertices[index++] = radius * sinTheta * sinPhi;
vertices[index++] = radius * cosPhi;
vertices[index++] = radius * cosTheta * sinPhi;
texCoords[index++] = (float)i / numSlices;
texCoords[index++] = (float)j / numStacks;
vertices[index++] = radius * sinTheta * sinPhi;
vertices[index++] = radius * cosPhi;
vertices[index++] = radius * cosTheta * sinPhi;
texCoords[index++] = (float)i / numSlices;
texCoords[index++] = (float)(j + 1) / numStacks;
vertices[index++] = radius * sinTheta * sinPhi;
vertices[index++] = radius * cosPhi;
vertices[index++] = radius * cosTheta * sinPhi;
texCoords[index++] = (float)(i + 1) / numSlices;
texCoords[index++] = (float)(j + 1) / numStacks;
vertices[index++] = radius * sinTheta * sinPhi;
vertices[index++] = radius * cosPhi;
vertices[index++] = radius * cosTheta * sinPhi;
texCoords[index++] = (float)(i + 1) / numSlices;
texCoords[index++] = (float)j / numStacks;
}
}
// 创建一个顶点缓冲对象
GLuint vertexBufferId;
glGenBuffers(1, &vertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
glBufferData(GL_ARRAY_BUFFER, vertices.length * 4, FloatBuffer.wrap(vertices), GL_STATIC_DRAW);
// 创建一个纹理坐标缓冲对象
GLuint texCoordBufferId;
glGenBuffers(1, &texCoordBufferId);
glBindBuffer(GL_ARRAY_BUFFER, texCoordBufferId);
glBufferData(GL_ARRAY_BUFFER, texCoords.length * 4, FloatBuffer.wrap(texCoords), GL_STATIC_DRAW);
// 创建一个程序对象
GLuint programId = createProgram(vertexShaderCode, fragmentShaderCode);
// 获取顶点属性的位置
int a_PositionLocation = glGetAttribLocation(programId, "a_Position");
int a_TexCoordLocation = glGetAttribLocation(programId, "a_TexCoord");
// 获取uniform变量的位置
int u_MVPMatrixLocation = glGetUniformLocation(programId, "u_MVPMatrix");
int u_TextureLocation = glGetUniformLocation(programId, "u_Texture");
// 启用顶点属性数组
glEnableVertexAttribArray(a_PositionLocation);
glEnableVertexAttribArray(a_TexCoordLocation);
// 绑定顶点属性数组
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
glVertexAttribPointer(a_PositionLocation, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, texCoordBufferId);
glVertexAttribPointer(a_TexCoordLocation, 2, GL_FLOAT, false, 0, 0);
// 激活纹理单元
glActiveTexture(GL_TEXTURE0);
// 绑定纹理对象
glBindTexture(GL_TEXTURE_2D, textureId);
// 设置uniform变量
glUniformMatrix4fv(u_MVPMatrixLocation, 1, false, mvpMatrix, 0);
glUniform1i(u_TextureLocation, 0);
// 绘制球面
glDrawArrays(GL_TRIANGLES, 0, numSlices * numStacks * 6);
// 禁用顶点属性数组
glDisableVertexAttribArray(a_PositionLocation);
glDisableVertexAttribArray(a_TexCoordLocation);
// 解绑纹理对象
glBindTexture(GL_TEXTURE_2D, 0);
// 删除纹理对象
glDeleteTextures(1, new int[]{textureId});
// 删除缓冲对象
glDeleteBuffers(1, new int[]{vertexBufferId, texCoordBufferId});
// 删除程序对象
glDeleteProgram(programId);
// 退出EGL和GLES2环境
// ...
}
运行结果
运行代码后,你将看到一个旋转的球面,球面上有一个发光的光晕。
总结
本篇中,我们学习了如何使用GLES2实现一个宇宙之光的效果。我们首先介绍了纹理贴图的技术,然后给出了实现代码。最后,我们展示了运行结果。