法线贴图可以让一些细节效果更好,所以我们把这个加到Project里了。
这里用的法线贴图。
教程中讲得听明白,改一改拼到我们之前画好的网格上就差不多了。
Normal mapping can have better effects when dealing with the details.
Then we use Normal Mapping.
This tutorial has a very clear structure. We will add it to our previous mesh.
从网上找了个水面的纹理和他的法线贴图
We get a texture and its normal map from the Internet.
用法线贴图多了法线切线副切线这些变量。原来5个变量现在变成14个变量了。
主要是需要添加切线副切线的值。之前那个数组需要修改。
我们新定义一个数组来存储所有的值: 10000个矩形网格,20000个三角形,每个三角形有三个点,每个点有十四个属性。
There are some additional attributes: normal, tangent and bitangent. Previously, we have only 5 attributes and now we have 14 attributes.
We have to add those values and we need a new array to store all the values.
static float quadVertices3[(SIZE_WATER-1)*(SIZE_WATER-1)*3*2*14] = {0.0f};
求切线副切线的方法在教程里讲得比较详细。
我们现在需要把之前的数组里的东西拿出来再用一次。根据之前数组存储方式来读取,数组索引有点乱。
It is clearly demonstrated how to get tangent and bitangent in the tutorial.
Now we have to reuse the wdata array. And we need to load these data according to its indices which is a little troublesome.
// positions
glm::vec3 npos1(wdata[data_index], wdata[data_index+1], wdata[data_index+2]);
glm::vec3 npos2(wdata[data_index+5], wdata[data_index+5+1], wdata[data_index+5+2]);
glm::vec3 npos3(wdata[data_index+SIZE_WATER*5], wdata[data_index+SIZE_WATER*5+1], wdata[data_index+SIZE_WATER*5+2]);
glm::vec3 npos4(wdata[data_index+(SIZE_WATER+1)*5], wdata[data_index+(SIZE_WATER+1)*5+1], wdata[data_index+(SIZE_WATER+1)*5+2]);
纹理坐标比较好处理
The texture coordinates are easy to handle.
// texture coordinates
glm::vec2 nuv1(0.0f, 0.0f);
glm::vec2 nuv2(1.0f, 0.0f);
glm::vec2 nuv3(0.0f, 1.0f);
glm::vec2 nuv4(1.0f, 1.0f);
法线在这里都是
Normal here is the same.
glm::vec3 nm2(0.0f, 0.0f, 1.0f);
主要是计算切线和副切线。教程里都有。是叉乘计算。
所有的代码:
How to compute tangent and bitangent are also well demonstrated in the tutorial which are
the cross products.
for(long i = 0; i < (SIZE_WATER-1)*(SIZE_WATER-1)*3*2*14; i = i +14*6)
{
if((data_index/5) % SIZE_WATER == SIZE_WATER-1)
data_index += 5;
/* -------------------- tangent ---------------------------------------*/
// positions
glm::vec3 npos1(wdata[data_index], wdata[data_index+1], wdata[data_index+2]);
glm::vec3 npos2(wdata[data_index+5], wdata[data_index+5+1], wdata[data_index+5+2]);
glm::vec3 npos3(wdata[data_index+SIZE_WATER*5], wdata[data_index+SIZE_WATER*5+1], wdata[data_index+SIZE_WATER*5+2]);
glm::vec3 npos4(wdata[data_index+(SIZE_WATER+1)*5], wdata[data_index+(SIZE_WATER+1)*5+1], wdata[data_index+(SIZE_WATER+1)*5+2]);
// texture coordinates
glm::vec2 nuv1(0.0f, 0.0f);
glm::vec2 nuv2(1.0f, 0.0f);
glm::vec2 nuv3(0.0f, 1.0f);
glm::vec2 nuv4(1.0f, 1.0f);
// calculate tangent/bitangent vectors of both triangles
glm::vec3 ntangent1, nbitangent1;
glm::vec3 ntangent2, nbitangent2;
// new tri 1
glm::vec3 nedge1 = npos2 - npos1;
glm::vec3 nedge2 = npos2 - npos4;
glm::vec2 ndeltaUV1 = nuv2 - nuv1;
glm::vec2 ndeltaUV2 = nuv2 - nuv4;
GLfloat nf = 1.0f / (ndeltaUV1.x * ndeltaUV2.y - ndeltaUV2.x * ndeltaUV1.y);
ntangent1.x = nf * (ndeltaUV2.y * nedge1.x - ndeltaUV1.y * nedge2.x);
ntangent1.y = nf * (ndeltaUV2.y * nedge1.y - ndeltaUV1.y * nedge2.y);
ntangent1.z = nf * (ndeltaUV2.y * nedge1.z - ndeltaUV1.y * nedge2.z);
ntangent1 = glm::normalize(ntangent1);
nbitangent1.x = nf * (-ndeltaUV2.x * nedge1.x + ndeltaUV1.x * nedge2.x);
nbitangent1.y = nf * (-ndeltaUV2.x * nedge1.y + ndeltaUV1.x * nedge2.y);
nbitangent1.z = nf * (-ndeltaUV2.x * nedge1.z + ndeltaUV1.x * nedge2.z);
nbitangent1 = glm::normalize(nbitangent1);
// new tri 2
nedge1 = npos3 - npos1;
nedge2 = npos3 - npos4;
ndeltaUV1 = nuv3 - nuv1;
ndeltaUV2 = nuv3 - nuv4;
nf = 1.0f / (ndeltaUV1.x * ndeltaUV2.y - ndeltaUV2.x * ndeltaUV1.y);
ntangent2.x = nf * (ndeltaUV2.y * nedge1.x - ndeltaUV1.y * nedge2.x);
ntangent2.y = nf * (ndeltaUV2.y * nedge1.y - ndeltaUV1.y * nedge2.y);
ntangent2.z = nf * (ndeltaUV2.y * nedge1.z - ndeltaUV1.y * nedge2.z);
ntangent2 = glm::normalize(ntangent2);
nbitangent2.x = nf * (-ndeltaUV2.x * nedge1.x + ndeltaUV1.x * nedge2.x);
nbitangent2.y = nf * (-ndeltaUV2.x * nedge1.y + ndeltaUV1.x * nedge2.y);
nbitangent2.z = nf * (-ndeltaUV2.x * nedge1.z + ndeltaUV1.x * nedge2.z);
nbitangent2 = glm::normalize(nbitangent2);
/* -----------------------------------------------------------*/
update2(data_index, i, ntangent1, nbitangent1);
update2(data_index+(SIZE_WATER+1)*5, i+14, ntangent1, nbitangent1);
update2(data_index+1*5, i+14*2, ntangent1, nbitangent1);
update2(data_index, i+14*3, ntangent2, nbitangent2);
update2(data_index+(SIZE_WATER+1)*5, i+14*4, ntangent2, nbitangent2);
update2(data_index+SIZE_WATER*5, i+14*5, ntangent2, nbitangent2);
data_index += 5;
}
顶点着色器:教程里的
Vertex shader used in tutorial.
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent;
out VS_OUT {
vec3 FragPos;
vec2 TexCoords;
vec3 TangentLightPos;
vec3 TangentViewPos;
vec3 TangentFragPos;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
vec3 T = normalize(normalMatrix * aTangent);
vec3 N = normalize(normalMatrix * aNormal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
vs_out.TangentLightPos = TBN * lightPos;
vs_out.TangentViewPos = TBN * viewPos;
vs_out.TangentFragPos = TBN * vs_out.FragPos;
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
片段着色器里面光照使用Blinn-Phong光照模型
Fragment shader used in the tutorial. Blinn-Phong Illumination model.
代码:
Code:
#version 330 core
out vec4 FragColor;
in VS_OUT {
vec3 FragPos;
vec2 TexCoords;
vec3 TangentLightPos;
vec3 TangentViewPos;
vec3 TangentFragPos;
} fs_in;
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
// obtain normal from normal map in range [0,1]
vec3 normal = texture(normalMap, fs_in.TexCoords).rgb;
// transform normal vector to range [-1,1]
normal = normalize(normal * 2.0 - 1.0); // this normal is in tangent space
// get diffuse color
vec3 color = texture(diffuseMap, fs_in.TexCoords).rgb;
// ambient
vec3 ambient = 0.1 * color;
// diffuse
vec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * color;
// specular
vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
vec3 specular = vec3(0.2) * spec;
FragColor = vec4(ambient + diffuse + specular, 1.0);
}