天天看點

音視訊開發之旅(42)-光照基礎(一)

目錄

  1. 光照相關基本知識
  2. 實踐
  3. 資料
  4. 收獲

效果如下:

音視訊開發之旅(42)-光照基礎(一)

一、光照相關基本知識

經典光照模型通過單獨計算光源成分得到綜合光照效果,然後添加到材質表面上特定點。這些成分包括環境光(Ambient Lighting)、漫反射光(Diffuse Lighting)和鏡面反射光(Specular Lighting)。
音視訊開發之旅(42)-光照基礎(一)

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傳給着色器使用。

漫反射光照是一種簡單的光照模型,它的公式是:

音視訊開發之旅(42)-光照基礎(一)

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 鏡面反射

隻有漫反射,再漂亮的模型也會失去光澤,我們必須找出一個方法來顯示模型的高光,這時應采用鏡面反射光照模型。鏡面反射光照模型的公式是:

音視訊開發之旅(42)-光照基礎(一)

H表示光線向量和視圖向量(可通過視圖矩陣轉換)之間的夾角正中的方向。稱為半角向量。Sexp是最終産生的鏡面顔色。N、L、Cmat和Cli的值與散射光方程式相同

音視訊開發之旅(42)-光照基礎(一)

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)
           
音視訊開發之旅(42)-光照基礎(一)

圖檔來自: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. 代碼先實作立方的繪制

由于在繪制立方體時,有涉及到内容較多,光照部分的具體實踐我們留在下一篇學習。在學習實踐過程中地圖非常關鍵,當不知道往哪走的時候,打開地圖,想一想目的地,很快就可以梳理清楚要走的路。一起加油。

感謝你的閱讀

下一篇我們繼續學習實踐光照部分内容,歡迎關注公衆号“音視訊開發之旅”,一起學習成長。

歡迎交流