青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

永遠也不完美的程序

不斷學習,不斷實踐,不斷的重構……

常用鏈接

統計

積分與排名

好友鏈接

最新評論

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


在前文中我盡可能地把我所理解Normal Map原理總結了一下,本續篇將從實踐部分繼續開始,各位看官盡情拍磚。——ZwqXin.com
上篇見:[shader復習與深入:Normal Map(法線貼圖)Ⅰ]
1. 怎樣獲得頂點的TBN
其實我覺得這個是實踐部分最麻煩的地方。OpenGL提供了諸如glNormal、normal-vbo之類的接口設置頂點的法線,然后在shader中以gl_Normal等方式取得頂點法線數據,但是沒有提供切線和副法線的。當然兩者只要其一就足夠了(另一者可通過叉乘和左/右手定則獲得)。因為要把TBN導入shader,干脆就設置attribute變量,記錄每個頂點的切線。切線一般就是相鄰頂點的差向量了(其實這有時候是非常繁重的工作)。
如果是通常的3DS模型的話,頂點法線是共頂點的面的面法線的加權,這樣法線就不一定垂直于某個面,即與切線不垂直。但只要它們還是近似垂直的,上篇提及的Gram-Schmidt 算法應該可以處理。或者在shader中,把法線與切線叉乘出副法線,再用法線與副法線叉乘得新的切線,也能確保兩兩垂直。這樣之前的TBN矩陣的轉置矩陣就能直接作為其逆矩陣,完成向量從模型坐標系往切線空間坐標系的變換了。
問題不只這樣。對于一些模型,共享頂點的三角面片面法線差角太大,這時候計算出的該頂點法線和切線就可能帶來麻煩。在橙書(OpenGL Shading Language)中,談及了切線必須是一致的(consistently),面片相鄰的頂點切線不應該差距太大。但若相鄰面片夾角太大,得到的該頂點法線就可能與“共享該頂點的面片”上的其他頂點的法線差異很大,從而切線也會相差很大,直接導致光向量等在這兩頂點的切線空間差異很大,插值的各個針對像素的光向量方向差異很大,與像素法線點乘的cos也會差異得很明顯(而現實中一般的凹凸面漫反射光線不會有太大方向差異)。解決方法是把該出了問題的頂點拆成兩個(原地拷貝,3DS模型就不用了- -),一個面片用一個,其法線只受所屬的面片的面法線決定(這樣最后會形成突出的邊緣,但夾角大的面片之間實際上就應該會是有這樣的效果吧)。
另一個問題,我們向shader傳入頂點法線切線,希望副法線由兩者叉乘得出。但既然叉乘就有個方向問題(結果可以有兩個方向,AXB與BXA是不一樣的,我以前弄shadow volume就曾被它這種特性作弄過)。AXB改成BXA實際上會導致凹凸感反向,原來凹的變凸了,原來凸的變凹了(要仔細比對,不然會有首因效應)。一般就用N X T吧,因為基本上都是這個順序的,結果也符合原Normal Map。
2. GLSL 1.2 Shader實現代碼
沒什么好說的,就是前面算法翻譯成GLSL。
Vertex Shader:
 
// vertex shader
uniform vec3 lightpos; //傳入光源的模型坐標吧
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;
   
//把光源和眼睛從模型空間轉換到視圖空間
   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);
   
   //將光源向量和視線向量轉換到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; //傳入光源的模型坐標吧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;   //把光源和眼睛從模型空間轉換到視圖空間   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);      //將光源向量和視線向量轉換到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是其模型坐標系下的坐標、向量,乘以ModelView矩陣(法線切線乘以ModelView矩陣的轉置逆矩陣)到了視圖空間(vlightPos,veyePos,pos,norm, vtangent);在視圖空間它們已經有了“世界”的概念了,因此可以平等地相互影響(在各自封閉的模型空間是享受不了的),可以作各種點乘叉乘加減乘除計算。
注意,lightPos,eyePos雖說是在其各自模型坐標系下定義的,但不對它們弄什么平移旋轉縮放操作的話,其模型矩陣就是一單位陣,此時其“世界坐標 == 模型坐標”。所以這時我可以當它是在世界空間定義的坐標(實際上一般我們都會在世界空間定義這兩個點)。(注意,前提是不對它們做模型變換。)
從以上量得到光源向量、視線向量后(它們在視圖空間),N、T叉乘得B(注意它們現在都在視圖空間),通過TBN矩陣逆矩陣把兩向量變換到當前頂點的切線空間,交給光柵去插值。
對以上有不理解的朋友,可能是沒看上篇:[shader復習與深入: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快速復習:Per Pixel Lighting(逐像素光照)]

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

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


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

本文來源于ZwqXin http://www.zwqxin.com/ , 轉載請注明
原文地址:http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map-2.html


 

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

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美精品18| 久久国产一区| 国产精品久久久久久久久久ktv| 久久riav二区三区| 久久久午夜视频| 久久久精品国产免大香伊| 久久深夜福利| 国产精品高潮呻吟久久| 亚洲精品一二三区| 欧美黄色aa电影| 欧美日本国产| 国产精品呻吟| 国内自拍亚洲| 国产精品mv在线观看| 国内成+人亚洲| 日韩视频永久免费| 亚洲六月丁香色婷婷综合久久| 亚洲黄色成人| 亚洲一级片在线观看| 欧美一级久久久久久久大片| 欧美一区二区三区视频在线观看| 久久婷婷蜜乳一本欲蜜臀| 麻豆乱码国产一区二区三区| 国产欧美日韩视频在线观看| 亚洲精品在线观看免费| 欧美99在线视频观看| 久久九九精品99国产精品| 久久精品毛片| 国产精品草草| 久久国产综合精品| 亚洲欧美经典视频| 国产精品久久久久久久午夜片| 亚洲性夜色噜噜噜7777| 免费看的黄色欧美网站| 欧美自拍偷拍午夜视频| 久久久久久高潮国产精品视| 久久精视频免费在线久久完整在线看| 亚洲毛片av| 国产精品视频一| 亚洲欧美激情精品一区二区| 一区二区三区久久精品| 国产精品久久久久久模特| 欧美日韩和欧美的一区二区| 亚洲黄一区二区| 一本色道久久88综合日韩精品 | 亚洲精品一区二区三区福利| 欧美成人综合一区| 欧美大片免费久久精品三p| 夜夜嗨av一区二区三区四区| 99热这里只有精品8| 国产精品揄拍500视频| 欧美成人国产一区二区| 欧美国产免费| 久久综合亚洲社区| 欧美视频在线观看一区二区| 久久精品二区亚洲w码| 欧美激情自拍| 亚洲高清一区二区三区| 国产免费一区二区三区香蕉精| 久热精品视频| 国产性色一区二区| 亚洲人成亚洲人成在线观看图片| 国产婷婷精品| 一区二区三区蜜桃网| 亚洲欧洲精品一区二区精品久久久| 亚洲视频免费观看| 亚洲一区二区高清| 欧美国产日韩精品免费观看| 久久久久久欧美| 国产一在线精品一区在线观看| 中文有码久久| 亚洲欧美www| 国产伦精品一区二区三区视频孕妇| 亚洲日本成人| 亚洲午夜视频在线观看| 欧美日韩一区二| 亚洲日本欧美日韩高观看| 亚洲欧美在线免费| 久久精品首页| 久久久久久69| 亚洲精品视频在线播放| 欧美成年人视频| 亚洲一区美女视频在线观看免费| 欧美在线观看日本一区| 亚洲视频在线观看| 欧美三级资源在线| 亚洲影视九九影院在线观看| 午夜精品久久久久久99热软件| 国产精品久久久久秋霞鲁丝| 午夜影院日韩| 亚洲福利精品| 亚洲欧美视频在线观看视频| 国产午夜精品全部视频在线播放| 久久男人av资源网站| 亚洲伦理精品| 性欧美激情精品| 亚洲男同1069视频| 欧美国产日韩一区二区在线观看| 亚洲三级国产| 男人的天堂亚洲| 久久精品观看| 午夜精彩国产免费不卡不顿大片| 影音先锋久久| 国产亚洲精品久久久久动| 欧美精品一区二区三区四区| 久久久久久久久久久久久久一区| 一本色道久久综合亚洲精品不| 久久五月天婷婷| 欧美视频导航| 一本一本久久a久久精品综合妖精 一本一本久久a久久精品综合麻豆 | 91久久精品国产91久久性色| 亚洲网站在线| 久久综合色综合88| 日韩香蕉视频| 一区二区不卡在线视频 午夜欧美不卡在 | 日韩网站在线观看| 影音先锋久久| 狠狠色丁香婷综合久久| 国产一区二区三区在线观看免费 | 亚洲作爱视频| 日韩午夜在线电影| 亚洲精选在线| 99精品热6080yy久久| 亚洲午夜一二三区视频| 亚洲欧美日韩天堂一区二区| 午夜在线成人av| 欧美国产免费| 国产精品99久久久久久有的能看| 亚洲午夜精品网| 欧美mv日韩mv国产网站app| 欧美国产一区在线| 欧美视频一区二区在线观看| 激情av一区| 欧美中文在线观看| 亚洲日本成人女熟在线观看| 欧美在线视频观看免费网站| 欧美另类人妖| 激情综合网激情| 午夜在线播放视频欧美| 日韩午夜激情av| 99精品国产一区二区青青牛奶| 亚洲午夜影视影院在线观看| 欧美成人午夜激情| 午夜国产精品视频| 国产精品婷婷午夜在线观看| 一区二区三区蜜桃网| 欧美韩日一区二区| 久久色中文字幕| 亚洲高清精品中出| 免费成人av| 欧美成人一区在线| 亚洲人成网站在线播| 美女网站久久| 欧美暴力喷水在线| 中国成人亚色综合网站| 99视频超级精品| 国产自产2019最新不卡| 久久免费黄色| 欧美精品网站| 亚洲一区二区在线播放| 亚洲欧美日韩国产| 国外成人在线视频| 亚洲丶国产丶欧美一区二区三区| 欧美激情中文字幕一区二区| 日韩视频免费大全中文字幕| 一区二区国产精品| 精品福利av| 亚洲视频一区二区| 91久久精品网| 午夜精品久久久久久久蜜桃app | 一区二区三区在线高清| 欧美激情一区在线| 国产精品久久久久久久久久直播 | 久久免费少妇高潮久久精品99| 久久综合久久综合久久综合| 亚洲在线视频免费观看| 欧美激情国产精品| 99国产一区二区三精品乱码| 一区二区三区高清在线观看| 国产主播喷水一区二区| 国产日韩欧美在线看| 久久亚洲一区| 久久这里只精品最新地址| 国产精品久久久久77777| 久久资源在线| 国际精品欧美精品| 亚洲国产精品久久| 亚洲国产综合在线| 亚洲国产欧美国产综合一区| 欧美在线免费观看| 午夜视频在线观看一区二区三区| 欧美久久成人| 一区二区日韩精品| 亚洲欧美日韩网| 国产亚洲一区在线| 久热国产精品视频| 欧美高清自拍一区| 亚洲一级在线观看| 老司机67194精品线观看| 国产一区二区精品久久91|