# 学习OpenGL ES之高级光照

### 获取示例代码

Blinn-Phong光照模型分为三个部分，环境光，漫反射光，高光（也可以理解为镜面反射光），将这三种光和物体本来的颜色融合，就可以计算出最终的颜色了。下面我们先来介绍这三种光的物理含义。

### 漫反射光

``````物体颜色 = (1.0, 0.0, 0.0) // r,g,b

``````

### Fragment Shader

``````precision highp float;

// 平行光
struct Directionlight {
vec3 direction;
vec3 color;
float indensity;
float ambientIndensity;
};

struct Material {
vec3 diffuseColor;
vec3 ambientColor;
vec3 specularColor;
float smoothness; // 0 ~ 1000 越高显得越光滑
};

varying vec3 fragNormal;
varying vec2 fragUV;
varying vec3 fragPosition;

uniform float elapsedTime;
uniform Directionlight light;
uniform Material material;
uniform vec3 eyePosition;
uniform mat4 normalMatrix;
uniform mat4 modelMatrix;

uniform sampler2D diffuseMap;

void main(void) {
vec3 normalizedLightDirection = normalize(-light.direction);
vec3 transformedNormal = normalize((normalMatrix * vec4(fragNormal, 1.0)).xyz);

// 计算漫反射
float diffuseStrength = dot(normalizedLightDirection, transformedNormal);
diffuseStrength = clamp(diffuseStrength, 0.0, 1.0);
vec3 diffuse = diffuseStrength * light.color * material.diffuseColor * light.indensity;

// 计算环境光
vec3 ambient = vec3(light.ambientIndensity) * material.ambientColor;

// 计算高光
vec4 eyeVertexPosition = modelMatrix * vec4(fragPosition, 1.0);
vec3 eyeVector = normalize(eyePosition - eyeVertexPosition.xyz);
vec3 halfVector = normalize(normalizedLightDirection + eyeVector);
float specularStrength = dot(halfVector, transformedNormal);
specularStrength = pow(specularStrength, material.smoothness);
vec3 specular = specularStrength * material.specularColor * light.color * light.indensity;

// 最终颜色计算
vec3 finalColor = diffuse + ambient + specular;

gl_FragColor = vec4(finalColor, 1.0);
}
``````

##### Directionlight
• `vec3 direction;`描述光照方向，和之前的`lightDirection`含义一致。
• `vec3 color;`光的颜色
• `float indensity;`光照强度，推荐值0~1，大于1的值可能会使物体大面积曝光过度。
• `float ambientIndensity;`环境光强度，可以放到结构体外，不是每个灯光必须的参数。
##### Material
• `vec3 diffuseColor;`物体的颜色，可以使用diffuse贴图来代替diffuseColor。
• `vec3 ambientColor;`环境光颜色，可以放到结构体外，如果你想所有的物体共享相同的环境光颜色的话。
• `vec3 specularColor;`高光颜色，我们可以为高光指定颜色，或者让高光的颜色和灯光颜色相同。
• `float smoothness;`平滑度，从0到1000。

``````uniform Directionlight light;
uniform Material material;
``````

##### 漫反射
``````// 计算漫反射
float diffuseStrength = dot(normalizedLightDirection, transformedNormal);
diffuseStrength = clamp(diffuseStrength, 0.0, 1.0);
vec3 diffuse = diffuseStrength * light.color * material.diffuseColor * light.indensity;
``````

`normalizedLightDirection`是反向并规范后的光照向量，`transformedNormal`是变换后的法线，利用他们的点乘计算出强度`diffuseStrength`，再将光照颜色，材质颜色，漫反射强度和光照强度相乘就得出了最终漫反射的颜色`diffuse`

##### 环境光
``````vec3 ambient = vec3(light.ambientIndensity) * material.ambientColor;
``````

##### 高光
``````// 计算高光
vec4 worldVertexPosition = modelMatrix * vec4(fragPosition, 1.0);
vec3 eyeVector = normalize(eyePosition - worldVertexPosition.xyz);
vec3 halfVector = normalize(normalizedLightDirection + eyeVector);
float specularStrength = dot(halfVector, transformedNormal);
specularStrength = pow(specularStrength, material.smoothness);
vec3 specular = specularStrength * material.specularColor * light.color * light.indensity;
``````

``````// 最终颜色计算
vec3 finalColor = diffuse + ambient + specular;

gl_FragColor = vec4(finalColor, 1.0);
``````

### OC代码

``````typedef struct  {
GLKVector3 direction;
GLKVector3 color;
GLfloat indensity;
GLfloat ambientIndensity;
} Directionlight;

typedef struct {
GLKVector3 diffuseColor;
GLKVector3 ambientColor;
GLKVector3 specularColor;
GLfloat smoothness; // 0 ~ 1000 越高显得越光滑
} Material;
``````
``````@property (assign, nonatomic) Directionlight light;
@property (assign, nonatomic) Material material;
``````

`viewDidLoad`中进行了初始化。

``````Directionlight defaultLight;
defaultLight.color = GLKVector3Make(1, 1, 1); // 白色的灯
defaultLight.direction = GLKVector3Make(1, -1, 0);
defaultLight.indensity = 1.0;
defaultLight.ambientIndensity = 0.1;
self.light = defaultLight;

Material material;
material.ambientColor = GLKVector3Make(1, 1, 1);
material.diffuseColor = GLKVector3Make(0.1, 0.1, 0.1);
material.specularColor = GLKVector3Make(1, 1, 1);
material.smoothness = 300;
self.material = material;
``````

``````#pragma mark - Arguments Adjust

- (IBAction)smoothnessAdjust:(UISlider *)sender {
Material _material = self.material;
_material.smoothness = sender.value;
self.material = _material;
}

- (IBAction)indensityAdjust:(UISlider *)sender {
Directionlight _light = self.light;
_light.indensity = sender.value;
self.light = _light;

}

- (IBAction)lightColorAdjust:(UISlider *)sender {
GLKVector3 yuv = GLKVector3Make(1.0, (cos(sender.value) + 1.0) / 2.0, (sin(sender.value) + 1.0) / 2.0);
Directionlight _light = self.light;
_light.color = [self colorFromYUV:yuv];
if (sender.value == sender.maximumValue) {
_light.color = GLKVector3Make(1, 1, 1);
}
self.light = _light;
sender.backgroundColor = [UIColor colorWithRed:_light.color.r green:_light.color.g blue:_light.color.b alpha:1.0];
}

- (IBAction)ambientColorAdjust:(UISlider *)sender {
GLKVector3 yuv = GLKVector3Make(1.0, (cos(sender.value) + 1.0) / 2.0, (sin(sender.value) + 1.0) / 2.0);
Material _material = self.material;
_material.ambientColor = [self colorFromYUV:yuv];
if (sender.value == sender.maximumValue) {
_material.ambientColor = GLKVector3Make(1, 1, 1);
}
self.material = _material;
sender.backgroundColor = [UIColor colorWithRed:_material.ambientColor.r green:_material.ambientColor.g blue:_material.ambientColor.b alpha:1.0];
}

- (IBAction)diffuseColorAdjust:(UISlider *)sender {
GLKVector3 yuv = GLKVector3Make(1.0, (cos(sender.value) + 1.0) / 2.0, (sin(sender.value) + 1.0) / 2.0);
Material _material = self.material;
_material.diffuseColor = [self colorFromYUV:yuv];
if (sender.value == sender.maximumValue) {
_material.diffuseColor = GLKVector3Make(1, 1, 1);
}
if (sender.value == sender.minimumValue) {
_material.diffuseColor = GLKVector3Make(0.1, 0.1, 0.1);
}
self.material = _material;
sender.backgroundColor = [UIColor colorWithRed:_material.diffuseColor.r green:_material.diffuseColor.g blue:_material.diffuseColor.b alpha:1.0];
}

- (IBAction)specularColorAdjust:(UISlider *)sender {
GLKVector3 yuv = GLKVector3Make(1.0, (cos(sender.value) + 1.0) / 2.0, (sin(sender.value) + 1.0) / 2.0);
Material _material = self.material;
_material.specularColor = [self colorFromYUV:yuv];
if (sender.value == sender.maximumValue) {
_material.specularColor = GLKVector3Make(1, 1, 1);
}
self.material = _material;
sender.backgroundColor = [UIColor colorWithRed:_material.specularColor.r green:_material.specularColor.g blue:_material.specularColor.b alpha:1.0];
}

- (GLKVector3)colorFromYUV:(GLKVector3)yuv {
float Cb, Cr, Y;
float R ,G, B;
Y = yuv.x * 255.0;
Cb = yuv.y * 255.0 - 128.0;
Cr = yuv.z * 255.0 - 128.0;

R = 1.402 * Cr + Y;
G = -0.344 * Cb - 0.714 * Cr + Y;
B = 1.772 * Cb + Y;

return GLKVector3Make(MIN(1.0, R / 255.0), MIN(1.0, G / 255.0), MIN(1.0, B / 255.0));
}
``````

``````- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
[super glkView:view drawInRect:rect];

[self.objects enumerateObjectsUsingBlock:^(GLObject *obj, NSUInteger idx, BOOL *stop) {
[obj.context active];
[obj.context setUniform1f:@"elapsedTime" value:(GLfloat)self.elapsedTime];
[obj.context setUniformMatrix4fv:@"projectionMatrix" value:self.projectionMatrix];
[obj.context setUniformMatrix4fv:@"cameraMatrix" value:self.cameraMatrix];
[obj.context setUniform3fv:@"eyePosition" value:self.eyePosition];
[obj.context setUniform3fv:@"light.direction" value:self.light.direction];
[obj.context setUniform3fv:@"light.color" value:self.light.color];
[obj.context setUniform1f:@"light.indensity" value:self.light.indensity];
[obj.context setUniform1f:@"light.ambientIndensity" value:self.light.ambientIndensity];
[obj.context setUniform3fv:@"material.diffuseColor" value:self.material.diffuseColor];
[obj.context setUniform3fv:@"material.ambientColor" value:self.material.ambientColor];
[obj.context setUniform3fv:@"material.specularColor" value:self.material.specularColor];
[obj.context setUniform1f:@"material.smoothness" value:self.material.smoothness];

[obj draw:obj.context];
}];
}
``````

``````newmtl mtlName
Ns 94.117647
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
d 1.0
illum 2
map_Kd XXX
map_Ks XXX
map_Ka XXX
map_Bump XXX
map_d XXX
``````
• `newmtl xxx`表示材质的名称，在obj文件中可以通过名称来引用材质。
• `Ns 94.117647` 高光调整参数，类似于我们的`smoothness`
• `Ka 1.000000 1.000000 1.000000` 环境光颜色
• `Kd 0.640000 0.640000 0.640000` 漫反射颜色
• `Ks 0.500000 0.500000 0.500000` 高光颜色
• `d 1.0` 溶解度，为0时完全透明，1完全不透明。
• `illum 2` 光照模式，0 禁止光照, 1 只有环境光和漫反射光，2 所有光照启用。
• `map_XX` map开头的都是各种颜色的贴图，如果有值，就是使用贴图来代替纯色，`map_Bump`表示法线贴图，在后面会有文章详细介绍。
了解到他们的含义后，我们就可以很轻易的将他们运用到Blinn-Phong光照模型中了。

OpenGL ES