造一面镜子
最基本的原理就是中学关于镜面反射的物理知识。镜子上显示的图像,可以看做镜像过去的另一个人所看到的的情景。使用OpenGL的术语来说就是把摄像机以镜子所在的平面做镜像,得到的镜像摄像机所观察到的世界,就是镜面上应该显示的内容。基本原理虽然很简单,但实现过程中也会遇到诸多问题。比如如何把镜像摄像机的渲染结果贴到镜面上,镜像摄像机被其他物体遮挡该如何处理。
摄像机
对摄像机进行了封装,主要是为了更方便的进行镜像。。主要功能是生成摄像机和镜像摄像机。摄像机使用向前的向量forward
,向上的向量up
和位置position
管理自身信息。镜像时将这三个变量分别求解出镜像值即可。求解向量的镜像主要使用了向量的反射公式。
@interface Camera : NSObject
@property (assign, nonatomic) GLKVector3 forward;
@property (assign, nonatomic) GLKVector3 up;
@property (assign, nonatomic) GLKVector3 position;
- (void)setupCameraWithEye:(GLKVector3)eye lookAt:(GLKVector3)lookAt up:(GLKVector3)up;
- (void)mirrorTo:(Camera *)targetCamera plane:(GLKVector4)plane;
- (GLKMatrix4)cameraMatrix;
@end
- (GLKVector3)reflect:(GLKVector3)sourceVector normalVector:(GLKVector3)normalVector {
CGFloat normalScalar = 2 * GLKVector3DotProduct(sourceVector, normalVector);
GLKVector3 scaledNormalVector = GLKVector3MultiplyScalar(normalVector, normalScalar);
GLKVector3 reflectVector = GLKVector3Subtract(sourceVector, scaledNormalVector);
return reflectVector;
}
- (void)mirrorTo:(Camera *)targetCamera plane:(GLKVector4)plane {
GLKVector3 planeNormal = GLKVector3Normalize(GLKVector3Make(plane.x, plane.y, plane.z));
GLKVector3 mirrorForward = GLKVector3Normalize([self reflect:self.forward normalVector:planeNormal]);
GLKVector3 mirrorUp = GLKVector3Normalize([self reflect:self.up normalVector:planeNormal]);
GLKVector3 planeCenter = GLKVector3MultiplyScalar(planeNormal, plane.w);
GLKVector3 eyeVector = GLKVector3Subtract(planeCenter, self.position);
CGFloat eyeVectorLength = GLKVector3Length(eyeVector);
eyeVector = GLKVector3Normalize(eyeVector);
GLKVector3 mirrorEyeVector = GLKVector3Normalize([self reflect:eyeVector normalVector:planeNormal]);
mirrorEyeVector = GLKVector3MultiplyScalar(mirrorEyeVector, eyeVectorLength);
GLKVector3 mirrorPosition = GLKVector3Subtract(planeCenter, mirrorEyeVector);
targetCamera.position = mirrorPosition;
targetCamera.up = mirrorUp;
targetCamera.forward = mirrorForward;
}
- (GLKMatrix4)cameraMatrix {
GLKVector3 eye = self.position;
GLKVector3 lookAt = GLKVector3Add(eye, self.forward);
return GLKMatrix4MakeLookAt(eye.x, eye.y, eye.z, lookAt.x, lookAt.y, lookAt.z, self.up.x, self.up.y, self.up.z);
}
渲染镜像摄像机内容
想要把镜像摄像机的内容渲染到镜面的平面上,我们需要建立一个新的Framebuffer,并且绑定一个纹理到它的颜色附件中。这样就可以把镜像摄像机的内容渲染到纹理了。
- (void)createTextureFramebuffer:(CGSize)framebufferSize {
glGenFramebuffers(1, &mirrorFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mirrorFramebuffer);
// 生成颜色缓冲区的纹理对象并绑定到framebuffer上
glGenTextures(1, &mirrorTexture);
glBindTexture(GL_TEXTURE_2D, mirrorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, framebufferSize.width, framebufferSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTexture, 0);
// 下面这段代码不使用纹理作为深度缓冲区。
GLuint depthBufferID;
glGenRenderbuffers(1, &depthBufferID);
glBindRenderbuffer(GL_RENDERBUFFER, depthBufferID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferSize.width, framebufferSize.height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferID);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
// framebuffer生成失败
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
制作场景
- (void)createWall {
UIImage *normalImage = [UIImage imageNamed:@"stoneFloor_NRM.png"];
GLKTextureInfo *normalMap = [GLKTextureLoader textureWithCGImage:normalImage.CGImage options:nil error:nil];
UIImage *diffuseImage = [UIImage imageNamed:@"stoneFloor.jpg"];
GLKTextureInfo *diffuseMap = [GLKTextureLoader textureWithCGImage:diffuseImage.CGImage options:nil error:nil];
NSString *objFile = [[NSBundle mainBundle] pathForResource:@"cube" ofType:@"obj"];
WavefrontOBJ *wall1 = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFile diffuseMap:diffuseMap normalMap:normalMap];
wall1.modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(0, 0, -16), GLKMatrix4MakeScale(15,15,1));
[self.objects addObject:wall1];
WavefrontOBJ *wall2 = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFile diffuseMap:diffuseMap normalMap:normalMap];
wall2.modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(0, 0, 16), GLKMatrix4MakeScale(15,15,1));
[self.objects addObject:wall2];
WavefrontOBJ *wall3 = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFile diffuseMap:diffuseMap normalMap:normalMap];
wall3.modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(16, 0, 0), GLKMatrix4MakeScale(1,15,15));
[self.objects addObject:wall3];
WavefrontOBJ *wall4 = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFile diffuseMap:diffuseMap normalMap:normalMap];
wall4.modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(-16, 0, 0), GLKMatrix4MakeScale(1,15,15));
[self.objects addObject:wall4];
}
- (void)createFloor {
UIImage *normalImage = [UIImage imageNamed:@"stoneFloor_NRM.png"];
GLKTextureInfo *normalMap = [GLKTextureLoader textureWithCGImage:normalImage.CGImage options:nil error:nil];
UIImage *diffuseImage = [UIImage imageNamed:@"stoneFloor.jpg"];
GLKTextureInfo *diffuseMap = [GLKTextureLoader textureWithCGImage:diffuseImage.CGImage options:nil error:nil];
NSString *objFile = [[NSBundle mainBundle] pathForResource:@"cube" ofType:@"obj"];
WavefrontOBJ *floor = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFile diffuseMap:diffuseMap normalMap:normalMap];
floor.modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(0, -1, 0), GLKMatrix4MakeScale(15,1,15));
[self.objects addObject:floor];
}
这是4堵墙和一个地板
创建镜子
- (void)createMirror {
CGSize framebufferSize = CGSizeMake(1024, 1024);
[self createTextureFramebuffer: framebufferSize];
NSString *vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"vertex" ofType:@".glsl"];
NSString *fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"frag_mirror" ofType:@".glsl"];
GLContext *mirrorGLContext = [GLContext contextWithVertexShaderPath:vertexShaderPath fragmentShaderPath:fragmentShaderPath];
self.mirror = [[Mirror alloc] initWithGLContext:mirrorGLContext texture:mirrorTexture];
self.mirror.modelMatrix = GLKMatrix4Multiply(GLKMatrix4MakeTranslation(0, 3.5, 0), GLKMatrix4MakeScale(8, 7, 1));
}
效果图