<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 第八課:基本著色 # 第八課:基礎光照模型 在第八課中,我們將學習光照模型的基礎知識。包括: - 物體離光源越近會越亮 - 直視反射光時會有高亮(鏡面反射) - 當光沒有直接照射物體時,物體會更暗(漫反射) - 用環境光簡化計算 不包括: - 陰影。這是個寬闊的主題,大到需要專題教程了。 - 類鏡面反射(包括水) - 任何復雜的光與物質的相互作用,像次表面散射(比如蠟) - 各向異性材料(比如拉絲的金屬) - 追求真實感的,基于物理的光照模型 - 環境光遮蔽(在洞穴里會更黑) - 顏色溢出(一塊紅色的地毯會映得白色天花板帶紅色) - 透明度 - 任何種類的全局光照(它包括了上面的所有) 總而言之:只講基礎。 ## 法向 過去的幾個教程中我們一直在處理法向,但是并不知道法向到底是什么。 ### 三角形法向 一個平面的法向是一個長度為1并且垂直于這個平面的向量。 一個三角形的法向是一個長度為1并且垂直于這個三角形的向量。通過簡單地將三角形兩條邊進行叉乘計算(向量a和b的叉乘結果是一個同時垂直于a和b的向量,記得?),然后歸一化:使長度為1。偽代碼如下: ``` <pre class="calibre16">``` triangle <span class="token1">(</span> v1<span class="token1">,</span> v2<span class="token1">,</span> v3 <span class="token1">)</span> edge1 <span class="token">=</span> v2<span class="token">-</span>v1 edge2 <span class="token">=</span> v3<span class="token">-</span>v1 triangle<span class="token1">.</span>normal <span class="token">=</span> <span class="token3">cross</span><span class="token1">(</span>edge1<span class="token1">,</span> edge2<span class="token1">)</span><span class="token1">.</span><span class="token3">normalize</span><span class="token1">(</span><span class="token1">)</span> ``` ``` 不要將法向(normal)和normalize()函數混淆。Normalize()函數是讓一個向量(任意向量,不一定必須是normal)除以其長度,從而使新長度為1。法向(normal)則是某一類向量的名字。 ### 頂點法向 引申開來:頂點的法向,是包含該頂點的所有三角形的法向的均值。這很方便——因為在頂點著色器中,我們處理頂點,而不是三角形;所以在頂點處有信息是很好的。并且在OpenGL中,我們沒有任何辦法獲得三角形信息。偽代碼如下: ``` <pre class="calibre16">``` vertex v1<span class="token1">,</span> v2<span class="token1">,</span> v3<span class="token1">,</span> <span class="token1">.</span><span class="token1">.</span><span class="token1">.</span><span class="token1">.</span> triangle tr1<span class="token1">,</span> tr2<span class="token1">,</span> tr3 <span class="token2">// all share vertex v1</span> v1<span class="token1">.</span>normal <span class="token">=</span> <span class="token3">normalize</span><span class="token1">(</span> tr1<span class="token1">.</span>normal <span class="token">+</span> tr2<span class="token1">.</span>normal <span class="token">+</span> tr3<span class="token1">.</span>normal <span class="token1">)</span> ``` ``` ### 在OpenGL中使用頂點法向 在OpenGL中使用法向很簡單。法向是頂點的屬性,就像位置,顏色,UV坐標等一樣;按處理其他屬性的方式處理即可。第七課的loadOBJ函數已經將它們從OBJ文件中讀出來了。 ``` <pre class="calibre16">``` GLuint normalbuffer<span class="token1">;</span> <span class="token3">glGenBuffers</span><span class="token1">(</span><span class="token6">1</span><span class="token1">,</span> <span class="token">&</span>normalbuffer<span class="token1">)</span><span class="token1">;</span> <span class="token3">glBindBuffer</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> normalbuffer<span class="token1">)</span><span class="token1">;</span> <span class="token3">glBufferData</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> normals<span class="token1">.</span><span class="token3">size</span><span class="token1">(</span><span class="token1">)</span> <span class="token">*</span> <span class="token3">sizeof</span><span class="token1">(</span>glm<span class="token1">:</span><span class="token1">:</span>vec3<span class="token1">)</span><span class="token1">,</span> <span class="token">&</span>normals<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">,</span> GL_STATIC_DRAW<span class="token1">)</span><span class="token1">;</span> ``` ``` 和 ``` <pre class="calibre16">``` <span class="token2">// 3rd attribute buffer : normals</span> <span class="token3">glEnableVertexAttribArray</span><span class="token1">(</span><span class="token6">2</span><span class="token1">)</span><span class="token1">;</span> <span class="token3">glBindBuffer</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> normalbuffer<span class="token1">)</span><span class="token1">;</span> <span class="token3">glVertexAttribPointer</span><span class="token1">(</span> <span class="token6">2</span><span class="token1">,</span> <span class="token2">// attribute</span> <span class="token6">3</span><span class="token1">,</span> <span class="token2">// size</span> GL_FLOAT<span class="token1">,</span> <span class="token2">// type</span> GL_FALSE<span class="token1">,</span> <span class="token2">// normalized?</span> <span class="token6">0</span><span class="token1">,</span> <span class="token2">// stride</span> <span class="token1">(</span>void<span class="token">*</span><span class="token1">)</span><span class="token6">0</span> <span class="token2">// array buffer offset</span> <span class="token1">)</span><span class="token1">;</span> ``` ``` 有這些準備就可以開始了。 ## 漫反射部分 ### 表面法向的重要性 當光源照射一個物體,其中重要的一部分光向各個方向反射。這就是“漫反射分量”。(我們不久將會看到光的其他部分去哪里了) ![](https://box.kancloud.cn/2015-11-02_5636f30535e9f.png) 當一定量的光線到達某表面,該表面根據光到達時的角度而不同程度地被照亮。 如果光線垂直于表面,它會聚在一小片表面上。如果它以一個傾斜角到達表面,相同的強度光照亮更大一片表面: ![](https://box.kancloud.cn/2015-11-02_5636f305417cc.png) 這意味著在斜射下,表面的點會較黑(但是記住,更多的點會被照射到,總光強度仍然是一樣的) 也就是說,當計算像素的顏色時,入射光和表面法向的夾角很重要。因此有: ``` <pre class="calibre16">``` <span class="token2">// Cosine of the angle between the normal and the light direction,</span> <span class="token2">// clamped above 0</span> <span class="token2">// - light is at the vertical of the triangle -> 1</span> <span class="token2">// - light is perpendicular to the triangle -> 0</span> float cosTheta <span class="token">=</span> <span class="token3">dot</span><span class="token1">(</span> n<span class="token1">,</span>l <span class="token1">)</span><span class="token1">;</span> color <span class="token">=</span> LightColor <span class="token">*</span> cosTheta<span class="token1">;</span> ``` ``` 在這段代碼中,n是表面法向,l是從表面到光源的單位向量(和光線方向相反。雖然不直觀,但能簡化數學計算)。 ### 注意正負 求cosTheta的公式有漏洞。如果光源在三角形后面,n和l方向相反,那么n.l是負值。這意味著colour=一個負數,沒有意義。因此這種情況須用clamp()將cosTheta賦值為0: ``` <pre class="calibre16">``` <span class="token2">// Cosine of the angle between the normal and the light direction,</span> <span class="token2">// clamped above 0</span> <span class="token2">// - light is at the vertical of the triangle -> 1</span> <span class="token2">// - light is perpendicular to the triangle -> 0</span> <span class="token2">// - light is behind the triangle -> 0</span> float cosTheta <span class="token">=</span> <span class="token3">clamp</span><span class="token1">(</span> <span class="token3">dot</span><span class="token1">(</span> n<span class="token1">,</span>l <span class="token1">)</span><span class="token1">,</span> <span class="token6">0</span><span class="token1">,</span><span class="token6">1</span> <span class="token1">)</span><span class="token1">;</span> color <span class="token">=</span> LightColor <span class="token">*</span> cosTheta<span class="token1">;</span> ``` ``` ### 材質顏色 當然,輸出顏色也依賴于材質顏色。在這幅圖像中,白光由綠、紅、藍光組成。當光碰到紅色材質時,綠光和藍光被吸收,只有紅光保留著。 ![](https://box.kancloud.cn/2015-11-02_5636f305530bd.png) 我們可以通過一個簡單的乘法來模擬: ``` <pre class="calibre16">``` color <span class="token">=</span> MaterialDiffuseColor <span class="token">*</span> LightColor <span class="token">*</span> cosTheta<span class="token1">;</span> ``` ``` ### 模擬光源 首先假設在空間中有一個點光源,它向所有方向發射光線,像蠟燭一樣。 對于該光源,我們的表面收到的光通量依賴于表面到光源的距離:越遠光越少。實際上,光通量與距離的平方成反比: ``` <pre class="calibre16">``` color <span class="token">=</span> MaterialDiffuseColor <span class="token">*</span> LightColor <span class="token">*</span> cosTheta <span class="token">/</span> <span class="token1">(</span>distance<span class="token">*</span>distance<span class="token1">)</span><span class="token1">;</span> ``` ``` 最后,需要另一個參數來控制光的強度。它可以被編碼到LightColor中(將在隨后的課程中講到),但是現在暫且只一個顏色值(如白色)和一個強度(如60瓦)。 ``` <pre class="calibre16">``` color <span class="token">=</span> MaterialDiffuseColor <span class="token">*</span> LightColor <span class="token">*</span> LightPower <span class="token">*</span> cosTheta <span class="token">/</span> <span class="token1">(</span>distance<span class="token">*</span>distance<span class="token1">)</span><span class="token1">;</span> ``` ``` ### 組合在一起 為了讓這段代碼運行,需要一些參數(各種顏色和強度)和更多代碼。 MaterialDiffuseColor簡單地從紋理中獲取。 LightColor和LightPower通過GLSL的uniform變量在著色器中設置。 cosTheta由n和l決定。我們可以在任意坐標系中表示它們,因為都是一樣的。這里選相機坐標系,是因為它計算光源位置簡單: ``` <pre class="calibre16">``` <span class="token2">// Normal of the computed fragment, in camera space</span> vec3 n <span class="token">=</span> <span class="token3">normalize</span><span class="token1">(</span> Normal_cameraspace <span class="token1">)</span><span class="token1">;</span> <span class="token2">// Direction of the light (from the fragment to the light)</span> vec3 l <span class="token">=</span> <span class="token3">normalize</span><span class="token1">(</span> LightDirection_cameraspace <span class="token1">)</span><span class="token1">;</span> ``` ``` Normal\_cameraspace和LightDirection\_cameraspace在頂點著色器中計算,然后傳給片斷著色器: ``` <pre class="calibre16">``` <span class="token2">// Output position of the vertex, in clip space : MVP * position</span> gl_Position <span class="token">=</span> MVP <span class="token">*</span> <span class="token3">vec4</span><span class="token1">(</span>vertexPosition_modelspace<span class="token1">,</span><span class="token6">1</span><span class="token1">)</span><span class="token1">;</span> <span class="token2">// Position of the vertex, in worldspace : M * position</span> Position_worldspace <span class="token">=</span> <span class="token1">(</span>M <span class="token">*</span> <span class="token3">vec4</span><span class="token1">(</span>vertexPosition_modelspace<span class="token1">,</span><span class="token6">1</span><span class="token1">)</span><span class="token1">)</span><span class="token1">.</span>xyz<span class="token1">;</span> <span class="token2">// Vector that goes from the vertex to the camera, in camera space.</span> <span class="token2">// In camera space, the camera is at the origin (0,0,0).</span> vec3 vertexPosition_cameraspace <span class="token">=</span> <span class="token1">(</span> V <span class="token">*</span> M <span class="token">*</span> <span class="token3">vec4</span><span class="token1">(</span>vertexPosition_modelspace<span class="token1">,</span><span class="token6">1</span><span class="token1">)</span><span class="token1">)</span><span class="token1">.</span>xyz<span class="token1">;</span> EyeDirection_cameraspace <span class="token">=</span> <span class="token3">vec3</span><span class="token1">(</span><span class="token6">0</span><span class="token1">,</span><span class="token6">0</span><span class="token1">,</span><span class="token6">0</span><span class="token1">)</span> <span class="token">-</span> vertexPosition_cameraspace<span class="token1">;</span> <span class="token2">// Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.</span> vec3 LightPosition_cameraspace <span class="token">=</span> <span class="token1">(</span> V <span class="token">*</span> <span class="token3">vec4</span><span class="token1">(</span>LightPosition_worldspace<span class="token1">,</span><span class="token6">1</span><span class="token1">)</span><span class="token1">)</span><span class="token1">.</span>xyz<span class="token1">;</span> LightDirection_cameraspace <span class="token">=</span> LightPosition_cameraspace <span class="token">+</span> EyeDirection_cameraspace<span class="token1">;</span> <span class="token2">// Normal of the the vertex, in camera space</span> Normal_cameraspace <span class="token">=</span> <span class="token1">(</span> V <span class="token">*</span> M <span class="token">*</span> <span class="token3">vec4</span><span class="token1">(</span>vertexNormal_modelspace<span class="token1">,</span><span class="token6">0</span><span class="token1">)</span><span class="token1">)</span><span class="token1">.</span>xyz<span class="token1">;</span> <span class="token2">// Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not.</span> ``` ``` 這段代碼看起來很牛,但它就是在第三課中學到的東西:矩陣。每個向量命名時,都嵌入了所在的空間名,這樣在跟蹤時更簡單。 你也應該這樣做。 M和V分別是模型和視圖矩陣,并且是用與MVP完全相同的方式傳給著色器。 ### 運行時間 現在有了編寫漫反射光源的一切必要條件。向前吧,刻苦努力地嘗試 ![](https://box.kancloud.cn/2015-11-02_5636f3089e7d7.gif) ### 結果 只包含漫反射分量時,我們得到以下結果(再次為無趣的紋理道歉): ![](https://box.kancloud.cn/2015-11-02_5636f3056a2bc.png) 這次結果比之前好,但感覺仍少了一些東西。特別地,Suzanne的背后完全是黑色的,因為我們使用clamp()。 ## 環境光分量 環境光分量是最華麗的優化。 我們期望的是Suzanne的背后有一點亮度,因為在現實生活中燈泡會照亮它背后的墻,而墻會反過來(微弱地)照亮物體的背后。 但計算它的代價大得可怕。 因此通常可以簡單地做點假光源取巧。實際上,直接讓三維模型發光,使它看起來不是完全黑即可。 可這樣完成: ``` <pre class="calibre16">``` vec3 MaterialAmbientColor <span class="token">=</span> <span class="token3">vec3</span><span class="token1">(</span><span class="token6">0.1</span><span class="token1">,</span><span class="token6">0.1</span><span class="token1">,</span><span class="token6">0.1</span><span class="token1">)</span> <span class="token">*</span> MaterialDiffuseColor<span class="token1">;</span> color <span class="token">=</span> <span class="token2">// Ambient : simulates indirect lighting</span> MaterialAmbientColor <span class="token">+</span> <span class="token2">// Diffuse : "color" of the object</span> MaterialDiffuseColor <span class="token">*</span> LightColor <span class="token">*</span> LightPower <span class="token">*</span> cosTheta <span class="token">/</span> <span class="token1">(</span>distance<span class="token">*</span>distance<span class="token1">)</span> <span class="token1">;</span> ``` ``` 來看看它的結果 ### 結果 好的,效果更好些了。如果要更好的結果,可以調整(0.1, 0.1, 0.1)值。 ![](https://box.kancloud.cn/2015-11-02_5636f30580083.png) ## 鏡面反射分量 反射光的剩余部分就是鏡面反射分量。這部分的光在表面有確定的反射方向。 ![](https://box.kancloud.cn/2015-11-02_5636f30599852.png) 如圖所示,它形成一種波瓣。在極端的情況下,漫反射分量可以為零,這樣波瓣非常非常窄(所有的光從一個方向反射),這就是鏡子。 *(的確可以調整參數值,得到鏡面;但這個例子中,鏡面唯一反射的只有光源,渲染結果看起來會很奇怪)* ``` <pre class="calibre16">``` <span class="token2">// Eye vector (towards the camera)</span> vec3 E <span class="token">=</span> <span class="token3">normalize</span><span class="token1">(</span>EyeDirection_cameraspace<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Direction in which the triangle reflects the light</span> vec3 R <span class="token">=</span> <span class="token3">reflect</span><span class="token1">(</span><span class="token">-</span>l<span class="token1">,</span>n<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Cosine of the angle between the Eye vector and the Reflect vector,</span> <span class="token2">// clamped to 0</span> <span class="token2">// - Looking into the reflection -> 1</span> <span class="token2">// - Looking elsewhere -> < 1</span> float cosAlpha <span class="token">=</span> <span class="token3">clamp</span><span class="token1">(</span> <span class="token3">dot</span><span class="token1">(</span> E<span class="token1">,</span>R <span class="token1">)</span><span class="token1">,</span> <span class="token6">0</span><span class="token1">,</span><span class="token6">1</span> <span class="token1">)</span><span class="token1">;</span> color <span class="token">=</span> <span class="token2">// Ambient : simulates indirect lighting</span> MaterialAmbientColor <span class="token">+</span> <span class="token2">// Diffuse : "color" of the object</span> MaterialDiffuseColor <span class="token">*</span> LightColor <span class="token">*</span> LightPower <span class="token">*</span> cosTheta <span class="token">/</span> <span class="token1">(</span>distance<span class="token">*</span>distance<span class="token1">)</span> <span class="token1">;</span> <span class="token2">// Specular : reflective highlight, like a mirror</span> MaterialSpecularColor <span class="token">*</span> LightColor <span class="token">*</span> LightPower <span class="token">*</span> <span class="token3">pow</span><span class="token1">(</span>cosAlpha<span class="token1">,</span><span class="token6">5</span><span class="token1">)</span> <span class="token">/</span> <span class="token1">(</span>distance<span class="token">*</span>distance<span class="token1">)</span><span class="token1">;</span> ``` ``` R是反射光的方向,E是視線的反方向(就像之前對“l”的假設);如果二者夾角很小,意味著視線與反射光線重合。 pow(cosAlpha,5)用來控制鏡面反射的波瓣。可以增大5來獲得更大的波瓣。 ### 最終結果 ![](https://box.kancloud.cn/2015-11-02_5636f305a6df9.png) 注意到鏡面反射使鼻子和眉毛更亮。 這個光照模型因為簡單,已被使用了很多年。但它有一些問題,所以被microfacet BRDF之類的基于物理的模型代替,后面將會講到。 在下節課中,我們將學習怎么提高VBO的性能。將是第一節中級課程!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看