# 学习OpenGL ES之Billboards

### 原理

``````- (GLfloat *)planeData {
static GLfloat planeData[] = {
-0.5,   0.5f,  0.0,   0,  0,  1, 0, 0,
-0.5f,  -0.5f,  0.0,  0,  0,  1, 0, 1,
0.5f,   -0.5f,  0.0,  0,  0,  1, 1, 1,
0.5,    -0.5f, 0.0,   0,  0,  1, 1, 1,
0.5f,  0.5f,  0.0,    0,  0,  1, 1, 0,
-0.5f,   0.5f,  0.0,  0,  0,  1, 0, 0,
};
return planeData;
}
``````

`NewPosition = CenterPosition + VecUp * OldPosition.x * Size.x + VecRight * OldPosition.y * Size.y`。这个公式的含义就是在摄像机的xy坐标系内生成一个长宽各与x，y轴平行的四边形。四边形长宽就是`Size`

``````vec3 cameraRightInWorldspace = vec3(cameraMatrix[0][0], cameraMatrix[1][0], cameraMatrix[2][0]);
vec3 cameraUpInWorldspace = vec3(0.0, 1.0, 0.0);
if (lockToYAxis == false) {
cameraUpInWorldspace = vec3(cameraMatrix[0][1], cameraMatrix[1][1], cameraMatrix[2][1]);
}
vec3 vertexPositionInWorldspace = billboardCenterPosition + cameraRightInWorldspace * position.x * billboardSize.x +
cameraUpInWorldspace * position.y * billboardSize.y;
fragPosition = vertexPositionInWorldspace;
gl_Position = vp * vec4(vertexPositionInWorldspace, 1.0);
``````

``````vec3 cameraRightInWorldspace = vec3(cameraMatrix[0][0], cameraMatrix[1][0], cameraMatrix[2][0]);
...
cameraUpInWorldspace = vec3(cameraMatrix[0][1], cameraMatrix[1][1], cameraMatrix[2][1]);
``````

``````vec3 vertexPositionInWorldspace = billboardCenterPosition + cameraRightInWorldspace * position.x * billboardSize.x +
cameraUpInWorldspace * position.y * billboardSize.y;
``````

``````void main(void) {
vec4 diffuseColor = texture2D(diffuseMap, fragUV);
if (diffuseColor.a == 0.0) {
}
vec3 finalColor = colorWithFog(diffuseColor.rgb);
gl_FragColor = vec4(finalColor, 1.0);
}
``````

### OC代码

OC代码基本复用平面几何体Plane的代码，在绘制的时候将Billboards特有的几个参数传递进去。完整代码在`Billboard.m`中。

``````[glContext setUniform2fv:@"billboardSize" value:self.billboardSize];
[glContext setUniform3fv:@"billboardCenterPosition" value:self.billboardCenterPosition];
[glContext setUniform1i:@"lockToYAxis" value:self.lockToYAxis];
``````

### 创建一些树

``````- (void)createTrees {
NSString *vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"vtx_billboard" ofType:@".glsl"];
NSString *fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"frag_billboard" ofType:@".glsl"];

for (int cycleTime = 0; cycleTime < 8; ++cycleTime) {
for (int angleSampleCount = 0; angleSampleCount < 9; ++angleSampleCount) {
float angle = rand() / (float)RAND_MAX * M_PI * 2.0;
float radius = rand() / (float)RAND_MAX * 70 + 40;
float xloc = cos(angle) * radius;
float zloc = sin(angle) * radius;
[self createTree: GLKVector3Make(xloc, 5, zloc)];
}
}
}

- (void)createTree:(GLKVector3)position {
GLKTextureInfo *grass = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"tree.png"].CGImage options:nil error:nil];
Billboard *tree = [[Billboard alloc] initWithGLContext:self.treeGlContext texture:grass];
[tree setBillboardCenterPosition:position];
[tree setBillboardSize:GLKVector2Make(6.0, 10.0)];
[tree setLockToYAxis:YES];