重新自学学习openGL 之光照材质

在现实世界里,每个物体会对光产生不同的反应。比如说,钢看起来通常会比陶瓷花瓶更闪闪发光,木头箱子也不会像钢制箱子那样对光产生很强的反射。每个物体对镜面高光也有不同的反应。有些物体反射光的时候不会有太多的散射(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 做底数来计算的.

它们模拟了现实世界中的真实材质。下面的图片展示了几种现实世界的材质对我们的立方体的影响:


这里是我根据上面材质做的一个模型


emerald

材质编码

这里我们只需要将上一节的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);

渲染结果


QQ20190806-164942.gif

综合上面代码demo源码

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)-光照

你可能感兴趣的:(重新自学学习openGL 之光照材质)