球体的渲染效果比较突出,所以本主题完成一个球体的绘制,并利用一些基本的渲染技巧实现渲染。
1. 球体绘制的数学模型;
2. 球体绘制的实现;
3. 球体的渲染的改进效果;
球体绘制的数学模型
球体绘制的数学模型
-
第一步:计算z坐标
- 球面绘制,首先按z方向,切分成多节,每节就是一个圆周;就是上图的1截出来的部分就是2所示的圆周。
- 截出的圆周有一个高,就是z-坐标;
- 圆周的高,其实就是这个圆周与圆心形成的锥形的角度决定了圆周的截断高度。这样可以轻松计算出
-
第二步:等高截的圆周的半径r
- 这个圆周半径也容易计算
- 这个圆周半径也容易计算
-
第三步:计算圆周上的x,y
- 圆周半径确定的情况下,x,y容易计算
- 圆周半径确定的情况下,x,y容易计算
代码实现
线计算u,v确定下的球面点坐标
- 代码中添加了一个球面总得半径,这个半径不影响上面的球面坐标的计算原理,但决定了球的大小。
glm::vec3 getPoint(GLfloat u, GLfloat v){
GLfloat r = 0.9f;
GLfloat pi = glm::pi<GLfloat>();
GLfloat z = r * std::cos(pi * u);
GLfloat x = r * std::sin(pi * u) * std::cos(2 * pi * v);
GLfloat y = r * std::sin(pi * u) * std::sin(2 * pi * v);
// std::cout << x << "," << y << "," << z << std::endl;
return glm::vec3(x, y, z);
}
循环u,v计算球面上的多个点
- 注意:
- u的取值区间
[0,180]
,v的取值区间[0,360]
。 - 计算的4个点,形成两个三角形的6个点。
- 我们假设u,v是
[0, 1]
,然后转换为[0, 180]
与[0, 360]
。 - 计算出来的点放入数组(其中内存拷贝的时候,内存地址的偏移基本单位与指针类型有关)
- u的取值区间
void createSphere(GLfloat *sphere, GLuint Longitude, GLuint Latitude){
// Longitude:经线切分个数
// Latitude:纬线切分个数
GLfloat lon_step = 1.0f/Longitude;
GLfloat lat_step = 1.0f/Latitude;
GLuint offset = 0;
for(int lat = 0; lat < Latitude; lat++){ // 纬线u
for(int lon = 0;lon < Longitude; lon++){ // 经线v
// 一次构造4个点,两个三角形,
glm::vec3 point1 = getPoint(lat * lat_step, lon * lon_step);
glm::vec3 point2 = getPoint((lat + 1) * lat_step, lon * lon_step);
glm::vec3 point3 = getPoint((lat + 1) * lat_step, (lon + 1) * lon_step);
glm::vec3 point4 = getPoint(lat * lat_step, (lon + 1) * lon_step);
memcpy(sphere + offset, glm::value_ptr(point1), 3 * sizeof(GLfloat));
offset += 3;
memcpy(sphere + offset, glm::value_ptr(point4), 3 * sizeof(GLfloat));
offset += 3;
memcpy(sphere + offset, glm::value_ptr(point3), 3 * sizeof(GLfloat));
offset += 3;
memcpy(sphere + offset, glm::value_ptr(point1), 3 * sizeof(GLfloat));
offset += 3;
memcpy(sphere + offset, glm::value_ptr(point3), 3 * sizeof(GLfloat));
offset += 3;
memcpy(sphere + offset, glm::value_ptr(point2), 3 * sizeof(GLfloat));
offset += 3;
}
}
// std::cout<<"offset:" << offset << std::endl;
}
调用代码
- 其中z等分,这里定义为:
- GLuint lats = 30;
- 每个圆周上的点的个数定义为:
- GLuint lons = 60;
GLfloat vertices[6 * 3 * lats * lons ];
std::cout<< "total:" << sizeof(vertices) << std::endl;
createSphere(vertices, lons, lats);
data.addData(vertices, sizeof(vertices), 0, 3); // 开启Shader第一个参数位置,顶点3个一组。
- 顶点着色器是缺省的
- 其中添加了简单的世界坐标变换mode,照相机坐标view,与透视变换perspective。
#version 410 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 perspective;
void main(){
gl_Position = perspective * view * model * vec4(aPos, 1.0);
}
- 片着色器是缺省的
#version 410 core
out vec4 FragColor;
void main(){
FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
- 效果- 绘制方式:
glDrawArrays(GL_LINE_LOOP, 0, 6 * lats * lons);
球体的网格线绘制
- 效果-绘制方式:
glDrawArrays(GL_POINTS, 0, 6 * lats * lons);
球体的网格点绘制
- 效果-绘制方式:
glDrawArrays(GL_TRIANGLES, 0, 6 * lats * lons);
球体的三角面绘制
改进与变化
颜色
- 使用球面坐标作为颜色
GLfloat vertices[6 * 3 * lats * lons ];
std::cout<< "total:" << sizeof(vertices) << std::endl;
createSphere(vertices, lons, lats);
data.addData(vertices, sizeof(vertices), 0, 3); // 开启Shader第一个参数位置,顶点3个一组。
// 直接使用切面坐标做颜色坐标
data.addData(vertices, sizeof(vertices), 1, 3); // 开启Shader第一个参数位置,顶点3个一组。
- 效果:代码中做了一个Model的缩小矩阵变换
model = glm::scale(model, glm::vec3(0.5f, 0.5f, 0.5f));
球体的颜色渲染
纹理
- 顶点着色器
#version 410 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 perspective;
out vec4 vColor;
void main(){
vColor = vec4(aColor, 1.0f);
gl_Position = perspective * view * model * vec4(aPos, 1.0);
}
- 片着色器
#version 410 core
uniform sampler2D aTexture;
out vec4 FragColor;
in vec4 vColor;
void main(){
// FragColor = vColor;
vec2 vTexture = vec2(vColor.x, vColor.y); // 因为x,y有正负,这个这里不处理,就形成对称纹理效果
FragColor = texture(aTexture, vTexture) * vColor;
}
- 纹理添加代码
///////////////////////////////////////////////////
// 添加纹理对象
GLint w = 0, h = 0, d = 0;
data.addTexture("texture.png",&w, &h, &d);
///////////////////////////////////////////////////
shader.initProgram();
shader.compileShaderFromFile("./glsl_script/glsl04_v_sphere_texture.glsl", GL_VERTEX_SHADER);
shader.compileShaderFromFile("./glsl_script/glsl04_f_sphere_texture.glsl", GL_FRAGMENT_SHADER);
shader.link();
shader.setUniform_1i("aTexture",0);
-
效果
球体的纹理渲染