• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            永遠(yuǎn)也不完美的程序

            不斷學(xué)習(xí),不斷實(shí)踐,不斷的重構(gòu)……

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            好友鏈接

            最新評(píng)論

            shader復(fù)雜與深入:Normal Map(法線貼圖)2


            在前文中我盡可能地把我所理解Normal Map原理總結(jié)了一下,本續(xù)篇將從實(shí)踐部分繼續(xù)開(kāi)始,各位看官盡情拍磚。——ZwqXin.com
            上篇見(jiàn):[shader復(fù)習(xí)與深入:Normal Map(法線貼圖)Ⅰ]
            1. 怎樣獲得頂點(diǎn)的TBN
            其實(shí)我覺(jué)得這個(gè)是實(shí)踐部分最麻煩的地方。OpenGL提供了諸如glNormal、normal-vbo之類的接口設(shè)置頂點(diǎn)的法線,然后在shader中以gl_Normal等方式取得頂點(diǎn)法線數(shù)據(jù),但是沒(méi)有提供切線和副法線的。當(dāng)然兩者只要其一就足夠了(另一者可通過(guò)叉乘和左/右手定則獲得)。因?yàn)橐裈BN導(dǎo)入shader,干脆就設(shè)置attribute變量,記錄每個(gè)頂點(diǎn)的切線。切線一般就是相鄰頂點(diǎn)的差向量了(其實(shí)這有時(shí)候是非常繁重的工作)。
            如果是通常的3DS模型的話,頂點(diǎn)法線是共頂點(diǎn)的面的面法線的加權(quán),這樣法線就不一定垂直于某個(gè)面,即與切線不垂直。但只要它們還是近似垂直的,上篇提及的Gram-Schmidt 算法應(yīng)該可以處理。或者在shader中,把法線與切線叉乘出副法線,再用法線與副法線叉乘得新的切線,也能確保兩兩垂直。這樣之前的TBN矩陣的轉(zhuǎn)置矩陣就能直接作為其逆矩陣,完成向量從模型坐標(biāo)系往切線空間坐標(biāo)系的變換了。
            問(wèn)題不只這樣。對(duì)于一些模型,共享頂點(diǎn)的三角面片面法線差角太大,這時(shí)候計(jì)算出的該頂點(diǎn)法線和切線就可能帶來(lái)麻煩。在橙書(shū)(OpenGL Shading Language)中,談及了切線必須是一致的(consistently),面片相鄰的頂點(diǎn)切線不應(yīng)該差距太大。但若相鄰面片夾角太大,得到的該頂點(diǎn)法線就可能與“共享該頂點(diǎn)的面片”上的其他頂點(diǎn)的法線差異很大,從而切線也會(huì)相差很大,直接導(dǎo)致光向量等在這兩頂點(diǎn)的切線空間差異很大,插值的各個(gè)針對(duì)像素的光向量方向差異很大,與像素法線點(diǎn)乘的cos也會(huì)差異得很明顯(而現(xiàn)實(shí)中一般的凹凸面漫反射光線不會(huì)有太大方向差異)。解決方法是把該出了問(wèn)題的頂點(diǎn)拆成兩個(gè)(原地拷貝,3DS模型就不用了- -),一個(gè)面片用一個(gè),其法線只受所屬的面片的面法線決定(這樣最后會(huì)形成突出的邊緣,但夾角大的面片之間實(shí)際上就應(yīng)該會(huì)是有這樣的效果吧)。
            另一個(gè)問(wèn)題,我們向shader傳入頂點(diǎn)法線切線,希望副法線由兩者叉乘得出。但既然叉乘就有個(gè)方向問(wèn)題(結(jié)果可以有兩個(gè)方向,AXB與BXA是不一樣的,我以前弄shadow volume就曾被它這種特性作弄過(guò))。AXB改成BXA實(shí)際上會(huì)導(dǎo)致凹凸感反向,原來(lái)凹的變凸了,原來(lái)凸的變凹了(要仔細(xì)比對(duì),不然會(huì)有首因效應(yīng))。一般就用N X T吧,因?yàn)榛旧隙际沁@個(gè)順序的,結(jié)果也符合原Normal Map。
            2. GLSL 1.2 Shader實(shí)現(xiàn)代碼
            沒(méi)什么好說(shuō)的,就是前面算法翻譯成GLSL。
            Vertex Shader:
             
            // vertex shader
            uniform vec3 lightpos; //傳入光源的模型坐標(biāo)吧
            uniform vec4 eyepos;
             
            varying vec3 lightdir;
            varying vec3 halfvec;
            varying vec3 norm;
            varying vec3 eyedir;
             
            attribute vec3 rm_Tangent;
             
            void main(void)
            {
               vec4 pos = gl_ModelViewMatrix * gl_Vertex;
               pos = pos / pos.w;
               
            //把光源和眼睛從模型空間轉(zhuǎn)換到視圖空間
               vec4 vlightPos = (gl_ModelViewMatrix * vec4(lightpos, 1.0));
               vec4 veyePos   = (gl_ModelViewMatrix * eyepos);
               
               lightdir = normalize(vlightPos.xyz - pos.xyz);
               vec3 eyedir = normalize(veyePos.xyz - pos.xyz);
               
              //模型空間下的TBN
               norm = normalize(gl_NormalMatrix * gl_Normal);
             
               vec3 vtangent  = normalize(gl_NormalMatrix * rm_Tangent);
             
               vec3 vbinormal = cross(norm,vtangent);
               
               //將光源向量和視線向量轉(zhuǎn)換到TBN切線空間
               lightdir.x = dot(vtangent,  lightdir);
               lightdir.y = dot(vbinormal, lightdir); 
               lightdir.z = dot(norm     , lightdir);
               lightdir = normalize(lightdir);
               
               eyedir.x = dot(vtangent,  eyedir);
               eyedir.y = dot(vbinormal, eyedir);
               eyedir.z = dot(norm     , eyedir);
               eyedir = normalize(eyedir);
               
               halfvec = normalize(lightdir + eyedir);
             
               gl_FrontColor = gl_Color;
               
               gl_TexCoord[0] = gl_MultiTexCoord0;
               
               gl_Position = ftransform();
            }
            // vertex shaderuniform vec3 lightpos; //傳入光源的模型坐標(biāo)吧uniform vec4 eyepos;varying vec3 lightdir;varying vec3 halfvec;varying vec3 norm;varying vec3 eyedir;attribute vec3 rm_Tangent;void main(void){   vec4 pos = gl_ModelViewMatrix * gl_Vertex;   pos = pos / pos.w;   //把光源和眼睛從模型空間轉(zhuǎn)換到視圖空間   vec4 vlightPos = (gl_ModelViewMatrix * vec4(lightpos, 1.0));   vec4 veyePos   = (gl_ModelViewMatrix * eyepos);      lightdir = normalize(vlightPos.xyz - pos.xyz);   vec3 eyedir = normalize(veyePos.xyz - pos.xyz);     //模型空間下的TBN   norm = normalize(gl_NormalMatrix * gl_Normal);   vec3 vtangent  = normalize(gl_NormalMatrix * rm_Tangent);   vec3 vbinormal = cross(norm,vtangent);      //將光源向量和視線向量轉(zhuǎn)換到TBN切線空間   lightdir.x = dot(vtangent,  lightdir);   lightdir.y = dot(vbinormal, lightdir);    lightdir.z = dot(norm     , lightdir);   lightdir = normalize(lightdir);      eyedir.x = dot(vtangent,  eyedir);   eyedir.y = dot(vbinormal, eyedir);   eyedir.z = dot(norm     , eyedir);   eyedir = normalize(eyedir);      halfvec = normalize(lightdir + eyedir);   gl_FrontColor = gl_Color;      gl_TexCoord[0] = gl_MultiTexCoord0;      gl_Position = ftransform();}
            傳入的lightPos,eyePos,gl_Vertex,gl_Normal,rm_Tangent是其模型坐標(biāo)系下的坐標(biāo)、向量,乘以ModelView矩陣(法線切線乘以ModelView矩陣的轉(zhuǎn)置逆矩陣)到了視圖空間(vlightPos,veyePos,pos,norm, vtangent);在視圖空間它們已經(jīng)有了“世界”的概念了,因此可以平等地相互影響(在各自封閉的模型空間是享受不了的),可以作各種點(diǎn)乘叉乘加減乘除計(jì)算。
            注意,lightPos,eyePos雖說(shuō)是在其各自模型坐標(biāo)系下定義的,但不對(duì)它們弄什么平移旋轉(zhuǎn)縮放操作的話,其模型矩陣就是一單位陣,此時(shí)其“世界坐標(biāo) == 模型坐標(biāo)”。所以這時(shí)我可以當(dāng)它是在世界空間定義的坐標(biāo)(實(shí)際上一般我們都會(huì)在世界空間定義這兩個(gè)點(diǎn))。(注意,前提是不對(duì)它們做模型變換。)
            從以上量得到光源向量、視線向量后(它們?cè)谝晥D空間),N、T叉乘得B(注意它們現(xiàn)在都在視圖空間),通過(guò)TBN矩陣逆矩陣把兩向量變換到當(dāng)前頂點(diǎn)的切線空間,交給光柵去插值。
            對(duì)以上有不理解的朋友,可能是沒(méi)看上篇:[shader復(fù)習(xí)與深入:Normal Map(法線貼圖)Ⅰ]
            fragment shader:
             
            //fragment shader
            uniform float shiness;
            uniform vec4 ambient, diffuse, specular;
             
            uniform sampler2D bumptex;
            uniform sampler2D basetex;
             
            float amb = 0.2;
            float diff = 0.2;
            float spec = 0.6;
             
            varying vec3 lightdir;
            varying vec3 halfvec;
            varying vec3 norm;
            varying vec3 eyedir;
             
            void main(void)
            {
               vec3 vlightdir = normalize(lightdir);
               vec3 veyedir = normalize(eyedir);
             
               vec3 vnorm =   normalize(norm);
               vec3 vhalfvec =  normalize(halfvec);  
               
               vec4 baseCol = texture2D(basetex, gl_TexCoord[0].xy); 
               
               //Normal Map里的像素normal定義于該像素的切線空間
               vec3 tbnnorm = texture2D(bumptex, gl_TexCoord[0].xy).xyz;
               
               tbnnorm = normalize((tbnnorm  - vec3(0.5))* 2.0); 
               
               float diffusefract =  max( dot(lightdir,tbnnorm) , 0.0); 
               float specularfract = max( dot(vhalfvec,tbnnorm) , 0.0);
               
               if(specularfract > 0.0){
               specularfract = pow(specularfract, shiness);
               }
               
               gl_FragColor = vec4(amb * ambient.xyz * baseCol.xyz
                             + diff * diffuse.xyz * diffusefract * baseCol.xyz
                             + spec * specular.xyz * specularfract ,1.0);
            }
            //fragment shaderuniform float shiness;uniform vec4 ambient, diffuse, specular;uniform sampler2D bumptex;uniform sampler2D basetex;float amb = 0.2;float diff = 0.2;float spec = 0.6;varying vec3 lightdir;varying vec3 halfvec;varying vec3 norm;varying vec3 eyedir;void main(void){   vec3 vlightdir = normalize(lightdir);   vec3 veyedir = normalize(eyedir);   vec3 vnorm =   normalize(norm);   vec3 vhalfvec =  normalize(halfvec);        vec4 baseCol = texture2D(basetex, gl_TexCoord[0].xy);       //Normal Map里的像素normal定義于該像素的切線空間   vec3 tbnnorm = texture2D(bumptex, gl_TexCoord[0].xy).xyz;      tbnnorm = normalize((tbnnorm  - vec3(0.5))* 2.0);       float diffusefract =  max( dot(lightdir,tbnnorm) , 0.0);    float specularfract = max( dot(vhalfvec,tbnnorm) , 0.0);      if(specularfract > 0.0){   specularfract = pow(specularfract, shiness);   }      gl_FragColor = vec4(amb * ambient.xyz * baseCol.xyz                 + diff * diffuse.xyz * diffusefract * baseCol.xyz                 + spec * specular.xyz * specularfract ,1.0);}
            注意把normal map里的normal由(0,1)映射回(-1,1)。baseCol得到的是基底紋理的像素顏色。其余部分就是per pixel lighting的東西了。[Shader快速?gòu)?fù)習(xí):Per Pixel Lighting(逐像素光照)]

            (上為底紋理和法線紋理,下為它們與某破壁模型合作的效果,紋理from planetpixelemporium.com)
             

            (我想是游戲最常用的用途:磚墻。我想是最常用的NormalMap,from NEHE)


            (自己把墻壁BaseMap放入Photoshop的normalMapFilter里弄的NormalMap,呃.....)

            本文來(lái)源于ZwqXin http://www.zwqxin.com/ , 轉(zhuǎn)載請(qǐng)注明
            原文地址:http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map-2.html


             

            posted on 2010-11-29 17:58 狂爛球 閱讀(2367) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 圖形編程

            久久久久国产一级毛片高清版| 狠狠色综合久久久久尤物| 国内精品久久久久久麻豆| 国产精品美女久久久m| 久久天堂AV综合合色蜜桃网| 久久乐国产综合亚洲精品| 亚洲国产精品无码久久久久久曰| 久久精品亚洲男人的天堂| 久久无码人妻精品一区二区三区| 国产精品伦理久久久久久 | 国产精品嫩草影院久久| 情人伊人久久综合亚洲| 久久国产三级无码一区二区| 久久性精品| 国产成人精品久久| 久久夜色精品国产噜噜麻豆| 狠狠狠色丁香婷婷综合久久俺| 日韩精品国产自在久久现线拍 | 久久91精品国产91| 伊人久久大香线焦AV综合影院 | 欧美性猛交xxxx免费看久久久| 久久久久国产一级毛片高清板| 亚洲天堂久久久| 国产麻豆精品久久一二三| 久久这里只精品国产99热| 久久人人爽人人澡人人高潮AV| 亚洲国产成人久久笫一页| 久久er99热精品一区二区| 伊人丁香狠狠色综合久久| 国产精品亚洲综合久久| 国产成人精品久久免费动漫| 国内精品久久久久久久久电影网 | AAA级久久久精品无码片| 久久精品国产色蜜蜜麻豆| 色欲久久久天天天综合网 | 亚洲国产精品成人AV无码久久综合影院| 亚洲精品成人久久久| 久久不射电影网| 亚洲国产精品一区二区久久hs| 久久久久成人精品无码| 久久精品草草草|