学习OpenGL ES之法线贴图

获取示例代码

什么是法线贴图

颜色如何转换成法线向量

RGB颜色数据的范围是`(0, 0, 0 )``(1,1,1)`，所以要把它转换成法线向量，需要所有元素乘以2再减去1，这样取值范围就变成了`(-1, -1, -1 )``(1,1,1)`，这是规范化后的向量该有的取值范围。那么这个时候是不是就能直接使用它计算光照强度了呢？

计算法线空间变换

``````Line10 = (U1 - U0) * Tangent + (V1 - V0) * Bitangent
Line20 = (U2 - U0) * Tangent + (V2 - V0) * Bitangent
``````

``````attribute vec3 tangent;
attribute vec3 bitangent;
...
varying vec3 fragTangent;
varying vec3 fragBitangent;
...
void main(void) {
...
fragTangent = tangent;
fragBitangent = bitangent;
gl_Position = mvp * position;
}
``````

• 添加接受法线贴图的uniform，`uniform sampler2D normalMap;`
• 将法线和切线都使用`normalMatrix`进行变换，从而变换到世界空间。
``````vec3 transformedNormal = normalize((normalMatrix * vec4(fragNormal, 1.0)).xyz);
vec3 transformedTangent = normalize((normalMatrix * vec4(fragTangent, 1.0)).xyz);
vec3 transformedBitangent = normalize((normalMatrix * vec4(fragBitangent, 1.0)).xyz);
``````
• 使用法线和切线组成TBN矩阵。
``````    mat3 TBN = mat3(
transformedTangent,
transformedBitangent,
transformedNormal
);
``````
• 取出法线贴图的值并使用TBN变换。
``````vec3 normalFromMap = (texture2D(normalMap, fragUV).rgb * 2.0 - 1.0);
transformedNormal = TBN * normalFromMap;
``````

为顶点计算切线

``````- (void)decompressToVertexArray {
NSInteger vertexCount = self.positionIndexData.length / sizeof(GLuint);
NSInteger triangleCount = vertexCount / 3;
for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) {
GLKVector3 positions[3];
GLKVector2 uvs[3];
GLKVector3 normals[3];
for (int vertexIndex = triangleIndex * 3; vertexIndex < triangleIndex * 3 + 3; ++vertexIndex) {
int positionIndex = 0;
[self.positionIndexData getBytes:&positionIndex range:NSMakeRange(vertexIndex * sizeof(GLuint), sizeof(GLuint))];
[self.positionData getBytes:&positions[vertexIndex % 3] range:NSMakeRange(positionIndex * 3 * sizeof(GLfloat), 3 * sizeof(GLfloat))];

int normalIndex = 0;
[self.normalIndexData getBytes:&normalIndex range:NSMakeRange(vertexIndex * sizeof(GLuint), sizeof(GLuint))];
[self.normalData getBytes:&normals[vertexIndex % 3] range:NSMakeRange(normalIndex * 3 * sizeof(GLfloat), 3 * sizeof(GLfloat))];

int uvIndex = 0;
[self.uvIndexData getBytes:&uvIndex range:NSMakeRange(vertexIndex * sizeof(GLuint), sizeof(GLuint))];
[self.uvData getBytes:&uvs[vertexIndex % 3] range:NSMakeRange(uvIndex * 2 * sizeof(GLfloat), 2 * sizeof(GLfloat))];
}
GLKVector3 deltaPos1 = GLKVector3Subtract(positions[1], positions[0]);
GLKVector3 deltaPos2 = GLKVector3Subtract(positions[2], positions[0]);
GLKVector2 deltaUV1 = GLKVector2Subtract(uvs[1], uvs[0]);
GLKVector2 deltaUV2 = GLKVector2Subtract(uvs[2], uvs[0]);
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);

GLKVector3 tangent = GLKVector3MultiplyScalar(GLKVector3Subtract(GLKVector3MultiplyScalar(deltaPos1, deltaUV2.y), GLKVector3MultiplyScalar(deltaPos2, deltaUV1.y)), r);
GLKVector3 bitangent = GLKVector3MultiplyScalar(GLKVector3Subtract(GLKVector3MultiplyScalar(deltaPos2, deltaUV1.x), GLKVector3MultiplyScalar(deltaPos1, deltaUV2.x)), r);

for (int i = 0; i< 3; ++i) {
[self.vertexData appendBytes:&positions[i] length:sizeof(GLKVector3)];
[self.vertexData appendBytes:&normals[i] length:sizeof(GLKVector3)];
[self.vertexData appendBytes:&uvs[i] length:sizeof(GLKVector2)];
[self.vertexData appendBytes:&tangent length:sizeof(GLKVector3)];
[self.vertexData appendBytes:&bitangent length:sizeof(GLKVector3)];
}
}
}
``````

``````GLKVector3 deltaPos1 = GLKVector3Subtract(positions[1], positions[0]);
GLKVector3 deltaPos2 = GLKVector3Subtract(positions[2], positions[0]);
GLKVector2 deltaUV1 = GLKVector2Subtract(uvs[1], uvs[0]);
GLKVector2 deltaUV2 = GLKVector2Subtract(uvs[2], uvs[0]);
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);

GLKVector3 tangent = GLKVector3MultiplyScalar(GLKVector3Subtract(GLKVector3MultiplyScalar(deltaPos1, deltaUV2.y), GLKVector3MultiplyScalar(deltaPos2, deltaUV1.y)), r);
GLKVector3 bitangent = GLKVector3MultiplyScalar(GLKVector3Subtract(GLKVector3MultiplyScalar(deltaPos2, deltaUV1.x), GLKVector3MultiplyScalar(deltaPos1, deltaUV2.x)), r);

``````

``````for (int i = 0; i< 3; ++i) {
[self.vertexData appendBytes:&positions[i] length:sizeof(GLKVector3)];
[self.vertexData appendBytes:&normals[i] length:sizeof(GLKVector3)];
[self.vertexData appendBytes:&uvs[i] length:sizeof(GLKVector2)];
[self.vertexData appendBytes:&tangent length:sizeof(GLKVector3)];
[self.vertexData appendBytes:&bitangent length:sizeof(GLKVector3)];
}
``````

修改顶点数据结构

``````- (void)genVAO {
glGenVertexArraysOES(1, &vao);
glBindVertexArrayOES(vao);

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);

GLuint positionAttribLocation = glGetAttribLocation(self.context.program, "position");
glEnableVertexAttribArray(positionAttribLocation);
GLuint colorAttribLocation = glGetAttribLocation(self.context.program, "normal");
glEnableVertexAttribArray(colorAttribLocation);
GLuint uvAttribLocation = glGetAttribLocation(self.context.program, "uv");
glEnableVertexAttribArray(uvAttribLocation);
GLuint tangentAttribLocation = glGetAttribLocation(self.context.program, "tangent");
glEnableVertexAttribArray(tangentAttribLocation);
GLuint bitangentAttribLocation = glGetAttribLocation(self.context.program, "bitangent");
glEnableVertexAttribArray(bitangentAttribLocation);

glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (char *)NULL);
glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (char *)NULL + 3 * sizeof(GLfloat));
glVertexAttribPointer(uvAttribLocation, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (char *)NULL + 6 * sizeof(GLfloat));
glVertexAttribPointer(tangentAttribLocation, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (char *)NULL + 8 * sizeof(GLfloat));
glVertexAttribPointer(bitangentAttribLocation, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (char *)NULL + 11 * sizeof(GLfloat));

glBindVertexArrayOES(0);
}
``````

贴图绑定

``````[glContext bindTexture:self.diffuseMap to:GL_TEXTURE0 uniformName:@"diffuseMap"];
[glContext bindTexture:self.normalMap to:GL_TEXTURE1 uniformName:@"normalMap"];
``````

``````- (void)createMonkeyFromObj {
UIImage *normalImage = [UIImage imageNamed:@"normal.png"];
GLKTextureInfo *normalMap = [GLKTextureLoader textureWithCGImage:normalImage.CGImage options:nil error:nil];
UIImage *diffuseImage = [UIImage imageNamed:@"texture.jpg"];
GLKTextureInfo *diffuseMap = [GLKTextureLoader textureWithCGImage:diffuseImage.CGImage options:nil error:nil];

NSString *objFilePath = [[NSBundle mainBundle] pathForResource:@"cube" ofType:@"obj"];
self.carModel = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFilePath diffuseMap:diffuseMap normalMap:normalMap];
self.carModel.modelMatrix = GLKMatrix4MakeRotation(- M_PI / 2.0, 0, 1, 0);