圆锥和圆柱光照和纹理
由于在圆锥的侧面中和底面圆中,重合的底面圆上的点的法向量是不同的,同时在纹理贴图时候,必有在同一个圆上位置,此处有两个顶点,其中一个的纹理索引是起始点,另一个是终点。
若将底面圆类在实现时,等同于被分为m个线段,则圆锥总的顶底需要一个底面圆心+底面圆周的点+侧面扇形上顶点+等同于m的圆锥顶点。即共需要顶点(m+1)*3+1个。
法向量和纹理:
在圆锥中:
顶点数组中,索引为1到m+1个的底面圆上的点、索引为0的底面圆心组成底面圆,法向量为垂直于圆平面单位向量。侧面上的法向量,例如对于索引为m+2个点,如下图,即侧面上扇形上的一个点P,和对应的圆锥顶点Q两个点的法向量,为垂直于两点的直线且与O、P、Q三点组成的平面,在同一个平面,我们可求出单位化后的法向量,其推导过程如下:
其纹理索引较为简单,相当于将整个纹理划分为不同的几个区域,然后映射到三角形上,即告诉opengl贴图时对应在图片上分割的位置。若纹理贴图重复一次,则位置为比例系数,若重复n此,则意味着总长度变为图片尺寸的n倍,即纹理索引需要乘以n。
具体实现代码:
void CMesh::CreateCone(float radius, int num_stacks, float height, float xz, float y)
{
double angle = 2 * M_PI / num_stacks;
//num_vertices = num_stacks + 2;//由于圆锥的底面圆上的顶点在底圆和侧面的法向量不一样,因此定点数要增加
num_vertices = (num_stacks+1) * 3 + 1;
num_indices = num_stacks * 2 * 3;//所画的三角形不变,因此索引不变
CMeshVertex* vertices = new CMeshVertex[num_vertices];
GLuint* indices = new GLuint[num_indices];
vertices[0].pos = vec3{ 0,0,0 };
vertices[0].normal = vec3{ 0,0,1 };
vertices[0].color = { 1.0f,1.0f,1.0f,1.0f };// vec4{ 1.0f,0.0f,0.0f,1.0f };
vertices[0].texcoord.x = xz / 2;
vertices[0].texcoord.y = y;
float nomalize_n = height * height / (height * height + radius * radius);
for (int i = 0; i <=num_stacks; ++i) {
vertices[i + 1].pos.x = cos(angle * i) * radius;
vertices[i + 1].pos.y = -sin(angle * i) * radius;
vertices[i + 1].pos.z = 0;
vertices[i + 1].normal = vec3{ 0,0,1 };
vertices[i + 1].color = { 1.0f,1.0f,1.0f,1.0f };// { 1.0f, 0.0f, 0.0f, 1.0f };
vertices[i + 1].texcoord.x = (float)i / (float)num_stacks * xz;
vertices[i + 1].texcoord.y = 0;
vertices[i + 2 + num_stacks].pos = vertices[i + 1].pos;
vertices[i + 2 + num_stacks].normal.x = cos(angle * i);
vertices[i + 2 + num_stacks].normal.y = -sin(angle * i);
vertices[i + 2 + num_stacks].normal.z = 0;
vertices[i + 2 + num_stacks].color = { 1.0f,1.0f,1.0f,1.0f };// { 0.5f, 0.0f, 0.5f, 1.0f };
vertices[i + 2 + num_stacks].texcoord = vertices[i + 1].texcoord;
vertices[i + 1 + (num_stacks + 1)*2].pos = vec3(0.0f, 0.0f, height);
vertices[i + 1 + (num_stacks +1)* 2].texcoord.x = (float)xz / 2;
vertices[i + 1 + (num_stacks +1)* 2].texcoord.y = (float)y / 2;
vertices[i + 1 + (num_stacks +1)* 2].color = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vertices[i + 1 + (num_stacks +1)* 2].normal.x = nomalize_n * cos(angle * i);
vertices[i + 1 + (num_stacks +1)* 2].normal.y = -nomalize_n * sin(angle * i);
vertices[i + 1 + (num_stacks +1)* 2].normal.z = radius *height/ (height*height + radius*radius);
if (i != num_stacks) {
indices[i * 6] = 0;
indices[i * 6 + 1] = i + 1;
indices[i * 6 + 2] = i + 2;
indices[i * 6 + 3] = i+1+(num_stacks+1)*2;
indices[i * 6 + 4] = i + 3 + num_stacks;
indices[i * 6 + 5] = i + 2 + num_stacks;
}
}
//vertices[(num_stacks + 1) * 2 + 1].color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
CreateGLResources(vertices, indices);
delete[] vertices;
delete[] indices;
}
对于圆柱:
侧面与底面圆交点,在上下底面中的法向量是垂直与两个平面的单位向量,在侧面中为对应的圆心指向它们各自位置的归一化向量。其实现更为简单,但是需要的顶点数量改为4*(m+1)+2个。
具体实现代码:
void CMesh::CreateCylinder(float radius, int num_stacks, float height, float xz, float y)
{
double angle = 2 * M_PI / num_stacks;
num_vertices = (num_stacks + 1) * 4 + 2;
num_indices = num_stacks * 3 * 4;
CMeshVertex* vertices = new CMeshVertex[num_vertices];
GLuint* indices = new GLuint[num_indices];
vertices[0].pos = vec3{ 0,0,0 };
vertices[0].normal = vec3{ 0,0,-1 };
vertices[0].color = { 1.0f,1.0f,1.0f,1.0f };// vec4{ 1.0f,0.0f,0.0f,0.0f };
vertices[0].texcoord.x = xz/2;
vertices[0].texcoord.y = y;
vertices[num_vertices - 1].pos = vec3{ 0,0,height };
vertices[num_vertices - 1].normal = vec3{ 0,0,1 };
vertices[num_vertices - 1].color = { 1.0f,1.0f,1.0f,1.0f };// { 0.0f, 0.5f, 0.5f, 0.0f };
vertices[num_vertices - 1].texcoord.x = xz/2;
vertices[num_vertices - 1].texcoord.y = y;
for (int i = 0; i <= num_stacks; ++i) {
vertices[i + 1].pos.x = cos(angle * i) * radius;
vertices[i + 1].pos.y = -sin(angle * i) * radius;
vertices[i + 1].pos.z = 0;
vertices[i + 1].normal = vec3{ 0,0,-1 };
vertices[i + 1].color = { 1.0f,1.0f,1.0f,1.0f };// { 1.0f,0.0f,0.0f,0.0f };
vertices[i + 1].texcoord.x = (float)i / (float)num_stacks * xz;
vertices[i + 1].texcoord.y = 0;
//侧面下的圆上点
vertices[i + 2 + num_stacks].pos = vertices[i + 1].pos;
vertices[i + 2 + num_stacks].normal.x = cos(angle * i);
vertices[i + 2 + num_stacks].normal.y = -sin(angle * i);
vertices[i + 2 + num_stacks].normal.z = 0;
vertices[i + 2 + num_stacks].color = { 1.0f,1.0f,1.0f,1.0f };// { 0.5f,0.0f,0.5f,0.0f };
vertices[i + 2 + num_stacks].texcoord = vertices[i + 1].texcoord;
//侧面上的圆上点
vertices[i + 1 + 2 * (num_stacks + 1 )] = vertices[i + 2 + num_stacks];
vertices[i + 1 + 2 * (num_stacks + 1)].pos.z = height;
vertices[i + 1 + 2 * (num_stacks + 1)].texcoord.y = y;
//最上边的一层圆上顶点
vertices[i + 1 + 3 * (num_stacks + 1)] = vertices[i + 1];
vertices[i + 1 + 3 * (num_stacks + 1)].pos.z = height;
vertices[i + 1 + 3 * (num_stacks + 1)].color = { 1.0f,1.0f,1.0f,1.0f };//{ 0.0f,0.5f,0.5f,0.0f };
vertices[i + 1 + 3 * (num_stacks + 1)].normal = vec3{ 0,0,1 };
if (i != num_stacks) {
indices[i * 12] = 0;
indices[i * 12 + 1] = i+1;
indices[i * 12 + 2] = i + 2;
indices[i * 12 + 3] = num_vertices - 1;
indices[i * 12 + 4] = i +2+ 3*(num_stacks+1);
indices[i * 12 + 5] = i + 1 + 3*(num_stacks+1);
indices[i * 12 + 6] = i + 2 +(num_stacks+1);
indices[i * 12 + 7] = i + 1 +(num_stacks+1);
indices[i * 12 + 8] = i +1+ 2*(num_stacks+1);
indices[i * 12 + 9] = i + 2+num_stacks+1;
indices[i * 12 + 10] = i+ 1+ 2*(num_stacks+1);
indices[i * 12 + 11] = i + 2 + 2*(num_stacks+1);
}
}
//vertices[num_stacks + 2].color = { 0.5f,0.0f,0.5f,0.0f };
CreateGLResources(vertices, indices);
delete[] vertices;
delete[] indices;
}
实现效果