对本系列绘制三维曲面的代码稍作修改,可实现三维球面的绘制,系列代码已开源。
开源链接:opengl_practice (github.com)
从二维平面到三维曲面
按照下图所示,将二维图片映射到三维球面上。图中数字表示经度和纬度。
着色器
片段着色器与绘制二维平面时区别不大,重点在于顶点着色器。
给顶点着色器传入两个值,维度和经度,顶点着色器由经纬度计算出纹理坐标和空间坐标。
顶点着色器:
#version 330 core
#define PI 3.1416
layout (location = 0) in vec2 uv;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
vec3 aPos;
void main()
{
aPos.z = cos(uv.x)*cos(uv.y);
aPos.x = cos(uv.x)*sin(uv.y);
aPos.y = sin(uv.x);
TexCoord = vec2(0.5*uv.y/PI, uv.x/PI + 0.5);
gl_Position = projection * view * model * vec4(aPos, 1.0f);
}
顶点数组与索引数组
每个顶点只用【维度,经度】两个值表示,启用图元重启,球面由多个三角条带构成。
// 纬度
float du = 1.0f;
int nu = (int)180 / du+1;
for (i = 0; i < nu; i++) {
u_arr[i] = (i*du - 90)/180.0f*PI;
}
// 经度
float dv = 1.0f;
int nv = (int)360 / dv + 1;
for (j = 0; j < nv; j++) {
v_arr[j] = j*dv/180.0f*PI;
}
// 二维经纬度数组分别转为一维数组
float* plt_u = calloc1d_float(nv * nu);
float* plt_v = calloc1d_float(nv * nu);
for (j = 0; j < nu; j++) {
for (i = 0; i < nv; i++) {
plt_v[j* nv+i] = v_arr[i];
plt_u[j* nv+i] = u_arr[j];
}
}
// 构建顶点数组
float* vertices = calloc1d_float(nv*nu*2);
for (i = 0; i < nv * nu; i += 1) {
vertices[2*i] = plt_u[i];
vertices[2*i+1] = plt_v[i];
}
// 构建索引数组,启用图元重启
int now_id, indices_n = (nu - 1) * (2* nv +1);
unsigned int* indices = calloc1d_uint(indices_n);
for (j = 0; j < nu - 1; j++) {
for (i = 0; i < nv; i++) {
now_id = 2 * (j * nv + i) + j;
indices[now_id] = j * nv + i;
indices[now_id + 1] = (j + 1) * nv + i;
if (i== nv -1)
indices[now_id + 2] = 0xFFFF;
}
}