音视频开发之旅(42)-光照基础(一)

目录

  1. 光照相关基本知识
  2. 实践
  3. 资料
  4. 收获

效果如下:


一、光照相关基本知识

经典光照模型通过单独计算光源成分得到综合光照效果,然后添加到材质表面上特定点。这些成分包括环境光(Ambient Lighting)、漫反射光(Diffuse Lighting)和镜面反射光(Specular Lighting)。

image

图片来自:基础光照

1.1 环境光(Ambient Lightiing)

环境光(Ambient Lightiing)不来自任何特定方向的光,在经典光照模型中会用一个常量来表示
使用时只需要对其片源着色器添加一个环境光常量,作为gl_Fragcolor的调制即可_

uniform vec4 ambient;
varying vec4 color;

void main{
    vec3 ambientLight = vec3(ambient);
    vec3 rgb = min(color.rgb * ambientLight,vec3(1.0));
    gl_FragColor = vec4(rgb,color.a);
}

1.2 漫反射光(Diffuse Lighting)

漫反射光(Diffuse Lighting)和镜面反射光都涉及到光源方向和眼睛观察方向。为此引入法向量的概念。

法向量是垂直于顶点表面的单位向量。

由于顶点本身并没有表面,它只是一个独立的点,我们可以利用它周围的顶点来计算出这个顶点的表面
就像顶点坐标一样,顶点方向量也作为一个location传给着色器使用。

漫反射光照是一种简单的光照模型,它的公式是:

N是顶点的单位法线,L是表示从顶点到光源的单位向量方向。Cmat是表面材料的颜色,Cli是光线的颜色,Cdiff是最终的散射颜色。

对应简化的着色器代码如下

    // Diffuse
    float diffuseStrength = 0.5; //漫反射强度
    vec3 unitNormal = normalize(vec3(u_ModelMatrix * vec4(a_normal, 1.0)));//顶点的单位法线
    vec3 lightDir = normalize(lightPos - fragPos);//从顶点到光源的单位向量方向
    float diff = max(dot(unitNormal, lightDir), 0.0);
    diffuse = diffuseStrength * diff * lightColor;

1.3 镜面反射

只有漫反射,再漂亮的模型也会失去光泽,我们必须找出一个方法来显示模型的高光,这时应采用镜面反射光照模型。镜面反射光照模型的公式是:


H表示光线向量和视图向量(可通过视图矩阵转换)之间的夹角正中的方向。称为半角向量。Sexp是最终产生的镜面颜色。N、L、Cmat和Cli的值与散射光方程式相同

image

图片来自:基础光照

对应简化的着色器代码如下:

    // Specular
    float specularStrength = 0.9;//镜面反射强度
    vec3 viewDir = normalize(viewPos - fragPos);//归一化的
    vec3 reflectDir = reflect(-lightDir, unitNormal);//光线向量和视图向量(可通过视图矩阵转换)之间的夹角正中的方向。称为半角向量
    float spec = pow(max(dot(unitNormal, reflectDir), 0.0), 16.0);
    specular = specularStrength * spec * lightColor;

二、 实践

为了更好的展示效果,我们立方体的基础上进行环境光照、漫反射光照、镜面反射光照。下面我们依次进行实践。

  1. 画一个立方体 加上图片纹理
  2. 加上环境光
  3. 加上漫反射光
  4. 加上镜面反射光

2.1 画个立方体并且渲染图片纹理

立方体的绘画我们可以采用画六个面的方式,也可以采用画一个面然后采用投影的方式。本篇我们才有后者实现

先来看下着色器, 比较简单,传入顶点坐标、纹理坐标、MVP矩阵以及纹理

//cube_vertex.glsl
precision mediump float;
attribute vec3 aPosition;
attribute vec2 aTexCoord;

uniform mat4 uMatrix;
varying vec2 v_texCoord;

void main()
{
    gl_Position = uMatrix * vec4(aPosition,1.0);
    v_texCoord = aTexCoord;
}
//cube_fragment.glsl

precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D uTexture;
void main()
{
    gl_FragColor = texture2D(uTexture, v_texCoord);
}

接着可以先画一个面,设置每个面上的顶点坐标和纹理坐标,然后根据透视投影变换投影出每个面上的画面。
关键代码如下:
首先确定每个面的顶点坐标和纹理坐标

 val vertexData = floatArrayOf(
                //position            //texture coord   
                -0.5f, -0.5f, -0.5f,   0.0f, 0.0f,
                0.5f, -0.5f, -0.5f,   1.0f, 0.0f,
                0.5f,  0.5f, -0.5f,   1.0f, 1.0f,
                0.5f,  0.5f, -0.5f,   1.0f, 1.0f,
                -0.5f,  0.5f, -0.5f,   0.0f, 1.0f, 
                -0.5f, -0.5f, -0.5f,   0.0f, 0.0f,

                -0.5f, -0.5f, 0.5f,    0.0f, 0.0f,
                0.5f, -0.5f, 0.5f,    1.0f, 0.0f, 
                0.5f,  0.5f, 0.5f,    1.0f, 1.0f,
                0.5f,  0.5f, 0.5f,    1.0f, 1.0f,
                -0.5f,  0.5f, 0.5f,    0.0f, 1.0f,
                -0.5f, -0.5f, 0.5f,    0.0f, 0.0f,

                -0.5f,  0.5f,  0.5f,   1.0f, 0.0f,
                -0.5f,  0.5f, -0.5f,   1.0f, 1.0f,
                -0.5f, -0.5f, -0.5f,   0.0f, 1.0f,
                -0.5f, -0.5f, -0.5f,   0.0f, 1.0f,
                -0.5f, -0.5f,  0.5f,   0.0f, 0.0f,
                -0.5f,  0.5f,  0.5f,   1.0f, 0.0f,

                0.5f,  0.5f,  0.5f,   1.0f, 0.0f,
                0.5f,  0.5f, -0.5f,   1.0f, 1.0f,
                0.5f, -0.5f, -0.5f,   0.0f, 1.0f,
                0.5f, -0.5f, -0.5f,   0.0f, 1.0f,
                0.5f, -0.5f,  0.5f,   0.0f, 0.0f,
                0.5f,  0.5f,  0.5f,   1.0f, 0.0f,

                -0.5f, -0.5f, -0.5f,   0.0f, 1.0f,
                0.5f, -0.5f, -0.5f,   1.0f, 1.0f,
                0.5f, -0.5f,  0.5f,   1.0f, 0.0f,
                0.5f, -0.5f,  0.5f,   1.0f, 0.0f,
                -0.5f, -0.5f,  0.5f,   0.0f, 0.0f,
                -0.5f, -0.5f, -0.5f,   0.0f, 1.0f,

                -0.5f, 0.5f, -0.5f,    0.0f, 1.0f,
                0.5f, 0.5f, -0.5f,    1.0f, 1.0f,
                0.5f, 0.5f,  0.5f,    1.0f, 0.0f,
                0.5f, 0.5f,  0.5f,    1.0f, 0.0f,
                -0.5f, 0.5f,  0.5f,    0.0f, 0.0f,
                -0.5f, 0.5f, -0.5f,    0.0f, 1.0f
        )
      var vertexArrayBuffer = ByteBuffer
                .allocateDirect(vertexData.size * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData)
        vertexArrayBuffer.position(0)

图片来自:OPENGL ES 案例03:COREANIMATION绘制立方体+旋转,其中图片中有个环节位移方向反了

下面来看下Render的实现

//在onSurfaceChanged 确定好透视投影矩阵和视图投影矩阵。
   
    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        GLES20.glViewport(0, 0, width, height)
        val whRadio = width / (height * 1.0f)
        Matrix.setIdentityM(projectionMatrix, 0)
        Matrix.perspectiveM(projectionMatrix, 0, 60f, whRadio, 1f, 100f)

        Matrix.setIdentityM(viewMatrix,0);
        Matrix.setLookAtM(viewMatrix,0,
        2f,0f,3f,
        0f,0f,0f,
        0f,1f,0f)
    }

//绘制
override fun onDrawFrame(gl: GL10?) {
        //这里由于用到深度测试需要清除GL_DEPTH_BUFFER_BIT
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)

        GLES20.glClearColor(0f, 0f, 0f, 1f)

        //必须要加深度测试
        GLES20.glEnable(GLES20.GL_DEPTH_TEST)


        Matrix.setIdentityM(modeMatrix, 0)


        //采用旋转的方式,只能采用旋转的方式,进行实现视角变换,达到移动的效果
        Matrix.rotateM(modeMatrix, 0, xRotation, 1f, 0f, 0f)
        Matrix.rotateM(modeMatrix, 0, yRotation, 0f, 1f, 0f)

        Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modeMatrix, 0)
        Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, mvpMatrix, 0)

        GLES20.glUseProgram(mProgram)

        //传mvp矩阵数据
        GLES20.glUniformMatrix4fv(uMatrixLoc, 1, false, mvpMatrix, 0)
        //传纹理数据
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, skyBoxTexture)
        GLES20.glUniform1i(uTextureLoc, 0)


        GLES20.glEnableVertexAttribArray(aPositionLoc)
        cubeLight.vertexArrayBuffer.position(0);
        GLES20.glVertexAttribPointer(aPositionLoc, CubeLight.POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT, false, CubeLight.STRIDE, cubeLight.vertexArrayBuffer)

        GLES20.glEnableVertexAttribArray(aTextureCoorLoc)
        cubeLight.vertexArrayBuffer.position(CubeLight.POSITION_COMPONENT_COUNT);
        GLES20.glVertexAttribPointer(aTextureCoorLoc, CubeLight.POSITION_TEXTURE_COUNT, GLES20.GL_FLOAT, false, CubeLight.STRIDE, cubeLight.vertexArrayBuffer)

        cubeLight.vertexArrayBuffer.position(0);


        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,36)
    }

具体介绍请看代码注释。完整代码已上传至 github https://github.com/ayyb1988/mediajourney

资料

《OpenGL编程指南》

基础光照
探究OpenGL光照模型的着色器实现
OpenGL_ES-光照(光照基础,漫反射,镜面反射)
NDK OpenGL ES 3.0 开发(九):光照基础
OPENGL ES 案例03:COREANIMATION绘制立方体+旋转

收获

  1. 了解了经典的冯氏光照模型
  2. 了解环境光照、漫反射光照、镜面反射光照的原理
  3. 拆分成多个环节逐步实现
  4. 代码先实现立方的绘制

由于在绘制立方体时,有涉及到内容较多,光照部分的具体实践我们留在下一篇学习。在学习实践过程中地图非常关键,当不知道往哪走的时候,打开地图,想一想目的地,很快就可以梳理清楚要走的路。一起加油。

感谢你的阅读
下一篇我们继续学习实践光照部分内容,欢迎关注公众号“音视频开发之旅”,一起学习成长。

欢迎交流

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • 光照-冯氏光照模型 环境 ambient 漫反射 diffuse 镜面反射 specular 环境光照(Ambie...
    li_礼光阅读 241评论 0 0
  • 在讲Unity光照模型之前,先介绍图形学中的两个基础光照模型原理,将会更利于我们理解和使用Unity中的光照模型。...
    Unity云中客阅读 3,576评论 1 6
  • 前言 这篇文章主要是为了了解在OpenGL ES中,光照的一些基本概念以及该如何进行光照计算,方便以后在用到光照时...
    伤心的EasyMan阅读 556评论 0 3
  • 颜色与光照的关系 我们看到的物体的颜色,实际是光照射物体后发射的光进入眼睛后感受到的颜色,而不是物体实际材料的颜色...
    瀚_阅读 613评论 0 0
  • 模拟正式光照环境生成图像时所需了解的相关概念 模拟真实光照环境生成图像的过程主要分为如下3个步骤 光源发射光线 光...
    全新的饭阅读 2,320评论 0 0