在现实世界里,每个物体会对光产生不同的反应。比如说,钢看起来通常会比陶瓷花瓶更闪闪发光,木头箱子也不会像钢制箱子那样对光产生很强的反射。每个物体对镜面高光也有不同的反应。有些物体反射光的时候不会有太多的散射(Scatter),因而产生一个较小的高光点,而有些物体则会散射很多,产生一个有着更大半径的高光点。如果我们想要在OpenGL中模拟多种类型的物体,我们必须为每个物体分别定义一个材质(Material)
属性。
知识点,
颜色计算 把光源的颜色与物体的颜色值相乘
从上一节,我们指定了一个物体和光的颜色,以及结合环境光和镜面强度分量,来定义物体的视觉输出. 因此, 我们可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting).
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
编码
在片段着色器中,我们创建一个结构体(Struct)来储存物体的材质属性。我们也可以把它们储存为独立的uniform值,但是作为一个结构体来储存会更有条理一些。
你可以看到,我们为每个冯氏光照模型的分量都定义一个颜色向量。ambient材质向量定义了在环境光照下这个物体反射得是什么颜色,通常这是和物体颜色相同的颜色。diffuse材质向量定义了在漫反射光照下物体的颜色。(和环境光照一样)漫反射颜色也要设置为我们需要的物体颜色。specular材质向量设置的是镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。最后,shininess影响镜面高光的散射/半径。
这四个元素定义了一个物体的材质,通过它们我们能够模拟很多现实世界中的材质。
下面列举一些材质
Name | Ambient | Diffuse | Specular | Shininess |
---|---|---|---|---|
emerald | 0.0215 0.1745 0.0215 | 0.07568 0.61424 0.07568 | 0.633 0.727811 0.633 | 0.6 |
jade | 0.135 0.2225 0.1575 | 0.54 0.89 0.63 | 0.316228 0.316228 0.316228 | 0.1 |
obsidian | 0.05375 0.05 0.06625 | 0.18275 0.17 0.22525 | 0.332741 0.328634 0.346435 | 0.3 |
pearl | 0.25 0.20725 0.20725 | 1 0.829 0.829 | 0.296648 0.296648 0.296648 | 0.088 |
ruby | 0.1745 0.01175 0.01175 | 0.61424 0.04136 0.04136 | 0.727811 0.626959 0.626959 | 0.6 |
turquoise | 0.1 0.18725 0.1745 | 0.396 0.74151 0.69102 | 0.297254 0.30829 0.306678 | 0.1 |
brass | 0.329412 0.223529 0.027451 | 0.780392 0.568627 0.113725 | 0.992157 0.941176 0.807843 | 0.21794872 |
bronze | 0.2125 0.1275 0.054 | 0.714 0.4284 0.18144 | 0.393548 0.271906 0.166721 | 0.2 |
chrome | 0.25 0.25 0.25 | 0.4 0.4 0.4 | 0.774597 0.774597 0.774597 | 0.6 |
copper | 0.19125 0.0735 0.0225 | 0.7038 0.27048 0.0828 | 0.256777 0.137622 0.086014 | 0.1 |
gold | 0.24725 0.1995 0.0745 | 0.75164 0.60648 0.22648 | 0.628281 0.555802 0.366065 | 0.4 |
silver | 0.19225 0.19225 0.19225 | 0.50754 0.50754 0.50754 | 0.508273 0.508273 0.508273 | 0.4 |
black plastic | 0.0 0.0 0.0 | 0.01 0.01 0.0 | 1 0.50 0.50 0.50 | .25 |
cyan plastic | 0.0 0.1 0.06 | 0.0 0.50980392 0.50980392 | 0.50196078 0.50196078 0.50196078 | .25 |
green plastic | 0.0 0.0 0.0 | 0.1 0.35 0.1 | 0.45 0.55 0.45 | .25 |
red plastic | 0.0 0.0 0.0 | 0.5 0.0 0. | 0 0.7 0.6 0.6 | .25 |
white plastic | 0.0 0.0 0.0 | 0.55 0.55 0.55 0.70 | 0.70 0.70 | .25 |
yellow plastic | 0.0 0.0 0.0 | 0.5 0.5 0.0 | 0.60 0.60 0.50 | .25 |
black rubber | 0.02 0.02 0.02 | 0.01 0.01 0.01 | 0.4 0.4 0.4 | . 078125 |
cyan rubber | 0.0 0.05 0.05 | 0.4 0.5 0.5 | 0.04 0.7 0.7 | .078125 |
green rubber | 0.0 0.05 0.0 | 0.4 0.5 0.4 | 0.04 0.7 0.04 | .078125 |
red rubber | 0.05 0.0 0.0 | 0.5 0.4 0.4 | 0.7 0.04 0.04 | .078125 |
white rubber | 0.05 0.05 0.05 | 0.5 0.5 0.5 | 0.7 0.7 0.7 | .078125 |
yellow rubber | 0.05 0.05 0.0 | 0.5 0.5 0.4 | 0.7 0.7 0.04 | .078125 |
这里需要注意 Shininess 是基于128 做底数来计算的.
它们模拟了现实世界中的真实材质。下面的图片展示了几种现实世界的材质对我们的立方体的影响:
这里是我根据上面材质做的一个模型
材质编码
这里我们只需要将上一节的shader 进行简单修改就行了.如下
precision lowp float;
uniform vec3 lightPos; ///光源位置
uniform vec3 lightColor; ///光源颜色
uniform vec3 viewPos;
//uniform vec3 ambientLight; ///环境光
varying lowp vec3 normal;
varying lowp vec3 FragPos;
//varying lowp vec3 vary_vertexColor;
struct Material{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
void main(){
// 环境光
vec3 ambient = lightColor * material.ambient;
// 漫反射
vec3 norm = normalize(normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// 镜面光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess*128.0);
vec3 specular = lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
gl_FragColor =vec4(result, 1.0);;
}
这里有个结构体,如何给结构体传值呢?
给结构体赋值,我们需要依次填充结构体
typedef union{
struct {
GLKVector3 ambient;
GLKVector3 diffuse;
GLKVector3 specular;
float shininess;
};
float m[10];
}Material;
Material material;
material.ambient = GLKVector3Make(0.0215, 0.1745, 0.0215);
material.diffuse =GLKVector3Make(0.07568, 0.61424, 0.07568);
material.specular =GLKVector3Make(0.633, 0.727811, 0.633);
material.shininess =0.6;
glUniform1fv(self.bindObject->uniforms[MaterialUniformLocationMaterialShininess],1,&material.shininess);
glUniform3fv(self.bindObject->uniforms[MaterialUniformLocationMaterialDiffuse], 1, &material.diffuse);
glUniform3fv(self.bindObject->uniforms[MaterialUniformLocationMaterialsSpecular], 1, &material.specular);
glUniform3fv(self.bindObject->uniforms[MaterialUniformLocationMaterialAmbient], 1, &material.ambient);
光属性
光源对环境光、漫反射和镜面光分量也具有着不同的强度。
假如我们设置光源对环境光、漫反射和镜面光分量是固定的话.
那么光源lightColor是vec3(1.0)
,代码会看起来像这样:
vec3 ambient = vec3(1.0) * material.ambient;
vec3 diffuse = vec3(1.0) * (diff * material.diffuse);
vec3 specular = vec3(1.0) * (spec * material.specular);
从上面代码能看出来,我们不能通过改变光的分量来改变材质的单一属性.为了改变材质的单一属性,我们就需要把光分成三部分,每一部分对应材质的一种分量.
定义光的结构如下
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
一个光源对它的ambient、diffuse和specular光照有着不同的强度。环境光照通常会设置为一个比较低的强度,因为我们不希望环境光颜色太过显眼。光源的漫反射分量通常设置为光所具有的颜色,通常是一个比较明亮的白色。镜面光分量通常会保持为vec3(1.0),以最大强度发光。注意我们也将光源的位置添加到了结构体中。
和材质uniform一样,我们需要更新片段着色器:
precision mediump float;
uniform vec3 lightPos; ///光源位置
uniform vec3 lightColor; ///光源颜色
uniform vec3 viewPos;
varying lowp vec3 normal;
varying lowp vec3 FragPos;
//varying lowp vec3 vary_vertexColor;
struct Material{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
struct Light{
vec3 lightPos;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
void main(){
// 环境光
vec3 ambient = light.ambient * material.ambient;
// 漫反射
vec3 norm = normalize(normal);
vec3 lightDir = normalize(light.lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * (diff * material.diffuse);
// 镜面光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess*128.0);
vec3 specular = light.specular * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
gl_FragColor =vec4(result, 1.0);;
}
这样我们就可以改变光来控制材质的单一属性了.
MaterialObject material;
material.ambient = GLKVector3Make(1.0f, 0.5f, 0.31f);
material.diffuse =GLKVector3Make(1.0f, 0.5f, 0.31f);
material.specular =GLKVector3Make(0.5f, 0.5f, 0.5f);
material.shininess =0.08988;
glUniform1fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialShininess],1,&material.shininess);
glUniform3fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialDiffuse], 1, &material.diffuse);
glUniform3fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialsSpecular], 1, &material.specular);
glUniform3fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialAmbient], 1, &material.ambient);
Light light;
light.lightPos = GLKVector3Make(2.0, 0.0,0);
light.ambient =GLKVector3Make(0.2,0.2,0.2);
light.diffuse =GLKVector3Make(0.5,0.5,0.5);
light.specular =GLKVector3Make(1.0,1.0,1.0);
glUniform3fv(self.bindObject->uniforms[LightUniformLocationLightPos], 1, &light.lightPos);
glUniform3fv(self.bindObject->uniforms[LightUniformLocationLightsSpecular], 1, &light.specular);
渲染结果
综合上面代码demo源码
#import "LightTestViewController.h"
#import "GLLightBindObject.h"
#import "CubeManager.h"
@interface LightTestViewController ()
@property (nonatomic ,strong) Vertex * vertexPostion ;
@property (nonatomic ,strong) Vertex * vertexColor ;
@property (nonatomic ,strong) Vertex * vertexNormal ;
@end
@implementation LightTestViewController
-(void)initSubObject{
//生命周期三秒钟
__weakSelf
self.bindObject = [GLLightBindObject new];
self.bindObject.uniformSetterBlock = ^(GLuint program) {
weakSelf.bindObject->uniforms[MVPMatrix] = glGetUniformLocation(self.shader.program, "u_mvpMatrix");
weakSelf.bindObject->uniforms[LightUniformLocationModel] = glGetUniformLocation(self.shader.program, "u_model");
weakSelf.bindObject->uniforms[LightUniformLocationInvermodel] = glGetUniformLocation(self.shader.program, "u_inverModel");
weakSelf.bindObject->uniforms[LightUniformLocationviewPos] = glGetUniformLocation(self.shader.program, "viewPos");
weakSelf.bindObject->uniforms[MaterialObjectUniformLocationMaterialAmbient] = glGetUniformLocation(self.shader.program, "material.ambient");
weakSelf.bindObject->uniforms[MaterialObjectUniformLocationMaterialDiffuse] = glGetUniformLocation(self.shader.program, "material.diffuse");
weakSelf.bindObject->uniforms[MaterialObjectUniformLocationMaterialsSpecular] = glGetUniformLocation(self.shader.program, "material.specular");
weakSelf.bindObject->uniforms[MaterialObjectUniformLocationMaterialShininess] = glGetUniformLocation(self.shader.program, "material.shininess");
weakSelf.bindObject->uniforms[LightUniformLocationLightPos] = glGetUniformLocation(self.shader.program, "light.lightPos");
weakSelf.bindObject->uniforms[LightUniformLocationLightAmbient] = glGetUniformLocation(self.shader.program, "light.ambient");
weakSelf.bindObject->uniforms[LightUniformLocationLightsSpecular] = glGetUniformLocation(self.shader.program, "light.specular");
weakSelf.bindObject->uniforms[LightUniformLocationLightDiffuse] = glGetUniformLocation(self.shader.program, "light.diffuse");
};
}
-(void)createShader{
__weakSelf
self.shader = [Shader new];
[self.shader compileLinkSuccessShaderName:self.bindObject.getShaderName completeBlock:^(GLuint program) {
[self.bindObject BindAttribLocation:program];
}];
if (self.bindObject.uniformSetterBlock) {
self.bindObject.uniformSetterBlock(self.shader.program);
}
}///消除0x502 操作
-(void)createTextureUnit{
}
-(void)loadVertex{
//顶点数据缓存
self.vertexPostion= [Vertex new];
int vertexNum =[CubeManager getNormalVertexNum];
[self.vertexPostion allocVertexNum:vertexNum andEachVertexNum:3];
for (int i=0; iuniforms[MaterialObjectUniformLocationMaterialShininess],1,&material.shininess);
glUniform3fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialDiffuse], 1, &material.diffuse);
glUniform3fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialsSpecular], 1, &material.specular);
glUniform3fv(self.bindObject->uniforms[MaterialObjectUniformLocationMaterialAmbient], 1, &material.ambient);
Light light;
light.lightPos = GLKVector3Make(2.0, 0.0,0);
light.ambient =GLKVector3Make(0.2,0.2,0.2);
light.diffuse =GLKVector3Make(0.5,0.5,0.5);
light.specular =GLKVector3Make(1.0,1.0,1.0);
glUniform3fv(self.bindObject->uniforms[LightUniformLocationLightPos], 1, &light.lightPos);
glUniform3fv(self.bindObject->uniforms[LightUniformLocationLightsSpecular], 1, &light.specular);
}
-(GLKMatrix4)getMVP{
GLfloat aspectRatio= CGRectGetWidth([UIScreen mainScreen].bounds) / CGRectGetHeight([UIScreen mainScreen].bounds);
GLKMatrix4 projectionMatrix =
GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(85.0f),
aspectRatio,
0.1f,
20.0f);
GLKMatrix4 modelviewMatrix =
GLKMatrix4MakeLookAt(
0.0, 0.0, 2.0, // Eye position
0.0, 0.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
return GLKMatrix4Multiply(projectionMatrix,modelviewMatrix);
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glClearColor(1, 1, 1, 1);
GLKMatrix4 mvp= [self getMVP];
static GLfloat angle=0;
angle ++ ;
// angle = 45;
GLKMatrix4 mode =GLKMatrix4MakeRotation(45*M_PI/180, 0, 1, 0);
glUniformMatrix4fv(self.bindObject->uniforms[MVPMatrix], 1, 0,mvp.m);
glUniformMatrix4fv(self.bindObject->uniforms[LightUniformLocationModel], 1, 0,mode.m);
bool isSuccess = YES;
mode = GLKMatrix4InvertAndTranspose(mode,&isSuccess);
glUniformMatrix4fv(self.bindObject->uniforms[LightUniformLocationInvermodel], 1, 0,mode.m);
GLKVector3 viewPos = GLKVector3Make(.0, 0.0,5.0);
glUniform3fv(self.bindObject->uniforms[LightUniformLocationviewPos], 1,viewPos.v);
GLfloat timer = angle/10.0;
GLKVector3 light = GLKVector3Make(sin(timer*2.0),sin( timer*0.7), sin(timer*1.3));
GLKVector3 diffuseColor = GLKVector3Multiply(light, GLKVector3Make(0.5, 0.5, 0.5));
GLKVector3 ambientColor = GLKVector3Multiply(light, GLKVector3Make(0.2, 0.2, 0.2));
glUniform3fv(self.bindObject->uniforms[LightUniformLocationLightDiffuse], 1, &diffuseColor);
glUniform3fv(self.bindObject->uniforms[LightUniformLocationLightAmbient], 1, &ambientColor);
[self.vertexPostion drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:[CubeManager getNormalVertexNum]];
}
@end
#import "GLBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface LightTestViewController : GLBaseViewController
@end
NS_ASSUME_NONNULL_END
#import "GLBaseBindObject.h"
NS_ASSUME_NONNULL_BEGIN
typedef enum {
LightBindAttribLocationBegin = BaseBindLocationEnd,
LightBindAttribLocationVertexColor,
LightBindAttribLocationNormal
}LightBindAttribLocation;
typedef union {
struct {
BaseBindAttribType baseBindAttrib;
GLKVector3 vertextColor;
};
float t[6];
}LightBindAttrib;
typedef union{
struct {
GLKVector3 lightPos;
GLKVector3 ambient;
GLKVector3 diffuse;
GLKVector3 specular;
};
float m[12];
}Light;
typedef union{
struct {
GLKVector3 ambient;
GLKVector3 diffuse;
GLKVector3 specular;
float shininess;
};
float m[10];
}MaterialObject;
typedef enum {
LightUniformLocationBegin = BaseUniformLocationEnd,
LightUniformLocationModel,
LightUniformLocationInvermodel,
LightUniformLocationviewPos,
LightUniformLocationLightPos, //
LightUniformLocationLightAmbient, //材质
LightUniformLocationLightDiffuse,
LightUniformLocationLightsSpecular,
MaterialObjectUniformLocationMaterialAmbient, //材质
MaterialObjectUniformLocationMaterialDiffuse,
MaterialObjectUniformLocationMaterialsSpecular,
MaterialObjectUniformLocationMaterialShininess
}LightUniformLocation;
@interface GLLightBindObject : GLBaseBindObject
@end
NS_ASSUME_NONNULL_END
#import "GLLightBindObject.h"
@implementation GLLightBindObject
-(void)BindAttribLocation:(GLuint)program{
glBindAttribLocation(program, BeginPosition, "beginPostion");
glBindAttribLocation(program,LightBindAttribLocationNormal, "aNormal");
}
-(NSString *)getShaderName{
return @"light";
}
@end
light.fsh
precision mediump float;
uniform vec3 lightPos; ///光源位置
uniform vec3 lightColor; ///光源颜色
uniform vec3 viewPos;
varying lowp vec3 normal;
varying lowp vec3 FragPos;
//varying lowp vec3 vary_vertexColor;
struct Material{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
struct Light{
vec3 lightPos;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
void main(){
// 环境光
vec3 ambient = light.ambient * material.ambient;
// 漫反射
vec3 norm = normalize(normal);
vec3 lightDir = normalize(light.lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * (diff * material.diffuse);
// 镜面光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess*128.0);
vec3 specular = light.specular * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
gl_FragColor =vec4(result, 1.0);;
}
light.vsh
precision lowp float;
attribute vec3 beginPostion; ///开始位置
//attribute vec3 vertexColor;
attribute vec3 aNormal; //法向量
uniform mat4 u_mvpMatrix;
uniform mat4 u_model;
uniform mat4 u_inverModel;
varying lowp vec3 normal;
varying lowp vec3 FragPos;
//varying lowp vec3 vary_vertexColor;
void main(){
gl_Position =u_mvpMatrix *u_model* vec4(beginPostion, 1.0);
// vary_vertexColor = vertexColor;
FragPos = vec3(u_model * vec4(beginPostion, 1.0));
normal = mat3(u_inverModel) * aNormal;;
}
以上是核心代码, 可以下载demo查看具体效果
源码地址 对应的demo是OpenGLZeroStudyDemo(7)-光照