『壹』 WebGL的頂點著色器編譯失敗,問題,怎麼解決
WebGL里有頂點著色器和片段著色器兩種著色器。無論哪一種都可以使用GLSL來編寫。頂點著色器和片段著色器是相互依賴的,缺一不可,並且首先被調用的是頂點著色器。
『貳』 GLSL初學求助著色器不起作用橙寶書的程序
OpenGL著色語言(GLSL―OpenGLShadingLanguage)是用來在OpenGL中著色編程的語言,也即開發人員寫的短小的自定義程序,他們是在圖形卡的GPU(GraphicProcessorUnit圖形處理單元)上執行的,代替了固定的渲染管線的一部分,使渲染管線中不同層次具有可編程型。比如:視圖轉換、投影轉換等。GLSL(GLShadingLanguage)的著色器代碼分成2個部分:VertexShader(頂點著色器)和Fragment(片斷著色器),有時還會有GeometryShader(幾何著色器)。負責運行頂點著色的是頂點著色器。它可以得到當前OpenGL中的狀態,GLSL內置變數進行傳遞。GLSL其使用C語言作為基礎高階著色語言,避免了使用匯編語言或硬體規格語言的復雜性。
『叄』 glsl語言和c語言的區別·也就是不同的地方有哪些,明確點。謝謝嘍
變數
GLSL的變數命名方式與C語言類似。變數的名稱可以使用字母,數字以及下劃線,但變數名不能以數字開頭,還有變數名不能以gl_作為前綴,這個是GLSL保留的前綴,用於GLSL的內部變數。當然還有一些GLSL保留的名稱是不能夠作為變數的名稱的。
基本類型
除了布爾型,整型,浮點型基本類型外,GLSL還引入了一些在著色器中經常用到的類型作為基本類型。這些基本類型都可以作為結構體內部的類型。如下表:
類型 描述
void 跟C語言的void類似,表示空類型。作為函數的返回類型,表示這個函數不返回值。
bool 布爾類型,可以是true 和false,以及可以產生布爾型的表達式。
int 整型 代表至少包含16位的有符號的整數。可以是十進制的,十六進制的,八進制的。
float 浮點型
bvec2 包含2個布爾成分的向量
bvec3 包含3個布爾成分的向量
bvec4 包含4個布爾成分的向量
ivec2 包含2個整型成分的向量
ivec3 包含3個整型成分的向量
ivec4 包含4個整型成分的向量
mat2 或者 mat2x2 2×2的浮點數矩陣類型
mat3或者mat3x3 3×3的浮點數矩陣類型
mat4x4 4×4的浮點矩陣
mat2x3 2列3行的浮點矩陣(OpenGL的矩陣是列主順序的)
mat2x4 2列4行的浮點矩陣
mat3x2 3列2行的浮點矩陣
mat3x4 3列4行的浮點矩陣
mat4x2 4列2行的浮點矩陣
mat4x3 4列3行的浮點矩陣
sampler1D 用於內建的紋理函數中引用指定的1D紋理的句柄。只可以作為一致變數或者函數參數使用
sampler2D 二維紋理句柄
sampler3D 三維紋理句柄
samplerCube cube map紋理句柄
sampler1DShadow 一維深度紋理句柄
sampler2DShadow 二維深度紋理句柄
結構體
結構體
結構體可以組合基本類型和數組來形成用戶自定義的類型。在定義一個結構體的同時,你可以定義一個結構體實例。或者後面再定義。
struct surface {float indexOfRefraction;
vec3 color;float turbulence;
} mySurface;
surface secondeSurface;
你可以通過=為結構體賦值,或者使用 ==,!=來判斷兩個結構體是否相等。
mySurface = secondSurface;
mySurface == secondSurface;
只有結構體中的每個成分都相等,那麼這兩個結構體才是相等的。訪問結構體的內部成員使用. 來訪問。
vec3 color = mySurface.color + secondSurface.color;
結構體至少包含一個成員。固定大小的數組也可以被包含在結構體中。GLSL的結構體不支持嵌套定義。只有預先聲明的結構體可以嵌套其中。
struct myStruct {
vec3 points[3]; //固定大小的數組是合法的
surface surf; //可以,之前已經定義了
struct velocity { //不合法float speed;
vec3 direction;
} velo;
subSurface sub; //不合法,沒有預先聲明;};struct subSurface { int id;
};
數組
GLSL中只可以使用一維的數組。數組的類型可以是一切基本類型或者結構體。下面的幾種數組聲明是合法的:
surface mySurfaces[];
vec4 lightPositions[8];
vec4 lightPos[] = light www.hnne.com Positions;const int numSurfaces = 5;
surface myFiveSurfaces[numSurfaces];float[5] values;
指定顯示大小的數組可以作為函數的參數或者使返回值,也可以作為結構體的成員.數組類型內建了一個length()函數,可以返回數組的長度。
lightPositions.length() //返回數組的大小 8
最後,你不能定義數組的數組。
修飾符
變數的聲明可以使用如下的修飾符。
修飾符 描述
const 常量值必須在聲明是初始化。它是只讀的不可修改的。
attribute 表示只讀的頂點數據,只用在頂點著色器中。數據來自當前的頂點狀態或者頂點數組。它必須是全局范圍聲明的,不能再函數內部。一個attribute可以是浮點數類型的標量,向量,或者矩陣。不可以是數組或則結構體
uniform 一致變數。在著色器執行期間一致變數的值是不變的。與const常量不同的是,這個值在編譯時期是未知的是由著色器外部初始化的。一致變數在頂點著色器和片段著色器之間是共享的。它也只能在全局范圍進行聲明。
varying 頂點著色器的輸出。例如顏色或者紋理坐標,(插值後的數據)作為片段著色器的只讀輸入數據。必須是全局范圍聲明的全局變數。可以是浮點數類型的標量,向量,矩陣。不能是數組或者結構體。
centorid varying 在沒有多重采樣的情況下,與varying是一樣的意思。在多重采樣時,centorid varying在光柵化的圖形內部進行求值而不是在片段中心的固定位置求值。
invariant (不變數)用於表示頂點著色器的輸出和任何匹配片段著色器的輸入,在不同的著色器中計算產生的值必須是一致的。所有的數據流和控制流,寫入一個invariant變數的是一致的。編譯器為了保證結果是完全一致的,需要放棄那些可能會導致不一致值的潛在的優化。除非必要,不要使用這個修飾符。在多通道渲染中避免z-fighting可能會使用到。
in 用在函數的參數中,表示這個參數是輸入的,在函數中改變這個值,並不會影響對調用的函數產生副作用。(相當於C語言的傳值),這個是函數參數默認的修飾符
out 用在函數的參數中,表示該參數是輸出參數,值是會改變的。
inout 用在函數的參數,表示這個參數即是輸入參數也是輸出參數。
內置變數
內置變數可以與固定函數功能進行交互。在使用前不需要聲明。頂點著色器可用的內置變數如下表:
名稱 類型 描述
gl_Color vec4 輸入屬性-表示頂點的主顏色
gl_SecondaryColor vec4 輸入屬性-表示頂點的輔助顏色
gl_Normal vec3 輸入屬性-表示頂點的法線值
gl_Vertex vec4 輸入屬性-表示物體空間的頂點位置
gl_MultiTexCoordn vec4 輸入屬性-表示頂點的第n個紋理的坐標
gl_FogCoord float 輸入屬性-表示頂點的霧坐標
gl_Position vec4 輸出屬性-變換後的頂點的位置,用於後面的固定的裁剪等操作。所有的頂點著色器都必須寫這個值。
gl_ClipVertex vec4 輸出坐標,用於用戶裁剪平面的裁剪
gl_PointSize float 點的大小
gl_FrontColor vec4 正面的主顏色的varying輸出
gl_BackColor vec4 背面主顏色的varying輸出
gl_FrontSecondaryColor vec4 正面的輔助顏色的varying輸出
gl_BackSecondaryColor vec4 背面的輔助顏色的varying輸出
gl_TexCoord[] vec4 紋理坐標的數組varying輸出
gl_FogFragCoord float 霧坐標的varying輸出
片段著色器的內置變數如下表:
名稱 類型 描述
gl_Color vec4 包含主顏色的插值只讀輸入
gl_SecondaryColor vec4 包含輔助顏色的插值只讀輸入
gl_TexCoord[] vec4 包含紋理坐標數組的插值只讀輸入
gl_FogFragCoord float 包含霧坐標的插值只讀輸入
gl_FragCoord vec4 只讀輸入,窗口的x,y,z和1/w
gl_FrontFacing bool 只讀輸入,如果是窗口正面圖元的一部分,則這個值為true
gl_PointCoord vec2 點精靈的二維空間坐標范圍在(0.0, 0.0)到(1.0, 1.0)之間,僅用於點圖元和點精靈開啟的情況下。
gl_FragData[] vec4 使用glDrawBuffers輸出的數據數組。不能與gl_FragColor結合使用。
gl_FragColor vec4 輸出的顏色用於隨後的像素操作
gl_FragDepth float 輸出的深度用於隨後的像素操作,如果這個值沒有被寫,則使用固定功能管線的深度值代替
表達式
操作符
GLSL語言的操作符與C語言相似。如下表(操作符的優先順序從高到低排列)
操作符 描述
() 用於表達式組合,函數調用,構造
[] 數組下標,向量或矩陣的選擇器
. 結構體和向量的成員選擇
++ – 前綴或後綴的自增自減操作符
+ – ! 一元操作符,表示正 負 邏輯非
* / 乘 除操作符
+ - 二元操作符 表示加 減操作
<> <= >= == != 小於,大於,小於等於, 大於等於,等於,不等於 判斷符
&& || ^^ 邏輯與 ,或, 異或
?: 條件判斷符
= += –= *= /= 賦值操作符
, 表示序列
像 求地址的& 和 解引用的 * 操作符不再GLSL中出現,因為GLSL不能直接操作地址。類型轉換操作也是不允許的。 位操作符(&,|,^,~, <<, >> ,&=, |=, ^=, <<=, >>=)是GLSL保留的操作符,將來可能會被使用。還有求模操作(%,%=)也是保留的。
數組訪問
數組的下標從0開始。合理的范圍是[0, size - 1]。跟C語言一樣。如果數組訪問越界了,那行為是未定義的。如果著色器的編譯器在編譯時知道數組訪問越界了,就會提示編譯失敗。
vec4 myColor, ambient, diffuse[6], specular[6];
myColor = ambient + diffuse[4] + specular[4];
構造函數
構造函數可以用於初始化包含多個成員的變數,包括數組和結構體。構造函數也可以用在表達式中。調用方式如下:
vec3 myNormal = vec3(1.0, 1.0, 1.0);
greenTint = myColor + vec3(0.0, 1.0, 0.0);
ivec4 myColor = ivec4(255);
還可以使用混合標量和向量的方式來構造,只要你的元素足以填滿該向量。
vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);
vec3 v = vec3(1.0, 10.0, 1.0);
vec3 v1 = vec3(v);
『肆』 glsl 頂點著色器 能實現波浪嗎
設置OpenGL ES環境
創建GLSurfaceView
顯示OpenGL圖形需要使用GLSurfaceView類像其任何View類意義添加Activity或Fragment通布局xml文件定義或者代碼創建實例
本教程我使用GLSurfaceView作唯View我Activity簡便我代碼創建GLSurfaceView實例並其傳入setContentView填充整手機屏幕ActivityonCreate:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GLSurfaceView view = new GLSurfaceView(this); setContentView(view);}
媒體效框架僅僅支持OpenGL ES2.0及版本所setEGLContextClientVersion 傳入2;
view.setEGLContextClientVersion(2);
確保GLSurfaceView僅僅必要候進行渲染我setRenderMode 進行設置:
view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
創建Renderer
Renderer負責渲染GLSurfaceView內容
創建類實現介面GLSurfaceView.Renderer我打算類命名EffectsRenderer添加構造函數並覆寫介面抽象:
public class EffectsRenderer implements GLSurfaceView.Renderer { public EffectsRenderer(Context context){ super(); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { } @Override public void onDrawFrame(GL10 gl) { }}
Activity調用setRenderer讓GLSurfaceView使用我創建Renderer:
view.setRenderer(new EffectsRenderer(this));
編寫Manifest文件
想要發布App谷歌商店AndroidManifest.xml文件添加語句:
確保app能安裝支持OpenGL ES2.0設備現OpenGL環境准備完畢
創建OpenGL平面
定義頂點
GLSurfaceView能直接顯示張照片照片首先應該轉化紋理應用OpenGL square本教程我創建2D平面並且具4頂點簡單我使用形現創建新類Square用代表形狀
public class Square {}
默認OpenGL系統坐標系原點4角坐標表示:
左角: (-1, -1) 右角:(1, -1) 右角:(1, 1) 左角:(-1, 1)
我使用OpenGL繪制所物體都應該由三角形決定畫形我需要兩具條公共邊三角形意味著些三角形坐標應該:
triangle 1: (-1, -1) (1, -1) (-1, 1) triangle 2: (1, -1) (-1, 1) (1, 1)
創建float數組代表些頂點:
private float vertices[] = { -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f,};
square定位紋理需要確定紋理頂點坐標創建另數組表示紋理頂點坐標:
private float textureVertices[] = { 0f,1f, 1f,1f, 0f,0f, 1f,0f};
創建緩沖區
些坐標數組應該轉變緩沖字元(byte buffer)OpenGL使用前接進行定義:
private FloatBuffer verticesBuffer;private FloatBuffer textureBuffer;
initializeBuffers初始化些緩沖區:使用ByteBuffer.allocateDirect創建緩沖區float4位元組我需要byte數組度應該float4倍
面使用ByteBuffer.nativeOrder定義底層本平台byte順序使用asFloatBufferByteBuffer轉化FloatBufferFloatBuffer創建我調用putfloat數組放入緩沖區調用position保證我由緩沖區進行讀取
private void initializeBuffers(){ ByteBuffer buff = ByteBuffer.allocateDirect(vertices.length * 4); buff.order(ByteOrder.nativeOrder()); verticesBuffer = buff.asFloatBuffer(); verticesBuffer.put(vertices); verticesBuffer.position(0); buff = ByteBuffer.allocateDirect(textureVertices.length * 4); buff.order(ByteOrder.nativeOrder()); textureBuffer = buff.asFloatBuffer(); textureBuffer.put(textureVertices); textureBuffer.position(0);}
創建著色器
著色器簡單運行GPU每單獨頂點C程序本教程我使用兩種著色器:頂點著色器片段著色器
頂點著色器代碼:
attribute vec4 aPosition; attribute vec2 aTexPosition; varying vec2 vTexPosition; void main() { gl_Position = aPosition; vTexPosition = aTexPosition; };
片段著色器代碼
precision mediump float; uniform. sampler2D uTexture; varying vec2 vTexPosition; void main() { gl_FragColor = texture2D(uTexture, vTexPosition); };
解OpenGL段代碼說熟悉能理解段代碼參考OpenGL documentation
-
『伍』 glsl 怎麼列印FBO的內容
什麼叫「列印」FBO?
FBO是frame buffer object,是一堆數據,你要列印這個?
不管怎麼樣,shader只管吃進頂點,然後計算,算出來的結果返回給rendertarget。其他的是在API(openGL)里做。你要列印什麼東西的話,就要去C++里調列印機。和shader完全沒有關系。
『陸』 如何調試GLSL Fragment shader
要斷點調試的話有些麻煩,要專門的插件我也沒用過
我一般是用顏色值來觀察的,把一些數值賦給頂點顏色,就能知道大概情況了
『柒』 opengl es的著色器語言 vec3(頂點)+vec3(向量)是什麼意思是每個分量相加嗎有什麼意義
是每個分量相加。就跟代數里通常的向量加法一樣,表示平移。需要注意的是著色器語言里向量乘法跟代數里的定義不一樣,也是各個分量單獨相乘。
『捌』 如何向GLSL中傳入多個紋理
設置OpenGL ES環境
創建GLSurfaceView
為了顯示OpenGL的圖形,你需要使用GLSurfaceView類,就像其他任何的View子類意義,你可以將它添加到你的Activity或Fragment之上,通過在布局xml文件中定義或者在代碼中創建實例。
在本次的教程中,我們使用GLSurfaceView作為唯一的View在我們的Activity中,因此,為了簡便,我們在代碼中創建GLSurfaceView的實例並將其傳入setContentView中,這樣它將會填充你的整個手機屏幕。Activity中的onCreate方法如下:
<code class=「hljs」 java=「」>protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GLSurfaceView view = new GLSurfaceView(this); setContentView(view);}</code>
因為媒體效果的框架僅僅支持OpenGL ES2.0及以上的版本,所以在setEGLContextClientVersion 方法中傳入2;
<code avrasm=「」 class=「hljs」>view.setEGLContextClientVersion(2);</code>
為了確保GLSurfaceView僅僅在必要的時候進行渲染,我們在setRenderMode 方法中進行設置:
<code avrasm=「」 class=「hljs」>view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);</code>
創建Renderer
Renderer負責渲染GLSurfaceView中的內容。
創建類實現介面GLSurfaceView.Renderer,在這里我們打算將這個類命名為EffectsRenderer,添加構造函數並覆寫介面中的抽象方法,如下:
<code class=「hljs」 java=「」>public class EffectsRenderer implements GLSurfaceView.Renderer { public EffectsRenderer(Context context){ super(); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { } @Override public void onDrawFrame(GL10 gl) { }}</code>
回到Activity中調用setRenderer方法,讓GLSurfaceView使用我們創建的Renderer:
<code class=「hljs」 cs=「」>view.setRenderer(new EffectsRenderer(this));</code>
編寫Manifest文件
如果你想要發布你的App到谷歌商店,在AndroidManifest.xml文件中添加如下語句:
<code class=「hljs」 xml=「」><uses-feature android:glesversion=「0x00020000」 android:required=「true」></uses-feature></code>
這會確保你的app只能被安裝在支持OpenGL ES2.0的設備之上。現在OpenGL環境准備完畢。
創建一個OpenGL平面
定義頂點
GLSurfaceView是不能直接顯示一張照片的,照片首先應該被轉化為紋理,應用在OpenGL square之上。在本次教程中,我將創建一個2D平面,並且具有4個頂點。為了簡單,我將使用一個長方形,現在,創建一個新的類Square,用它來代表形狀。
<code class=「hljs」 cs=「」>public class Square {}</code>
默認的OpenGL系統的坐標系中的原點是在中心,因此4個角的坐標可以表示為:
左下角: (-1, -1) 右下角:(1, -1) 右上角:(1, 1) 左上角:(-1, 1)
我們使用OpenGL繪制的所有的物體都應該是由三角形決定的,為了畫一個方形,我們需要兩個具有一條公共邊的三角形,那意味著這些三角形的坐標應該是:
triangle 1: (-1, -1), (1, -1), 和 (-1, 1) triangle 2: (1, -1), (-1, 1), 和 (1, 1)
創建一個float數組來代表這些頂點:
<code class=「hljs」 cpp=「」>private float vertices[] = { -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f,};</code>
為了在square上定位紋理,需要確定紋理的頂點坐標,創建另一個數組來表示紋理頂點的坐標:
<code class=「hljs」 cpp=「」>private float textureVertices[] = { 0f,1f, 1f,1f, 0f,0f, 1f,0f};</code>
創建緩沖區
這些坐標數組應該被轉變為緩沖字元(byte buffer)在OpenGL可以使用之前,接下來進行定義:
<code class=「hljs」 cs=「」>private FloatBuffer verticesBuffer;private FloatBuffer textureBuffer;</code>
在initializeBuffers方法中去初始化這些緩沖區:使用ByteBuffer.allocateDirect來創建緩沖區,因為float是4個位元組,那麼我們需要的byte數組的長度應該為float的4倍。
下面使用ByteBuffer.nativeOrder方法來定義在底層的本地平台上的byte的順序。使用asFloatBuffer方法將ByteBuffer轉化為FloatBuffer,在FloatBuffer被創建後,我們調用put方法來將float數組放入緩沖區,最後,調用position方法來保證我們是由緩沖區的開頭進行讀取。
<code avrasm=「」 class=「hljs」>private void initializeBuffers(){ ByteBuffer buff = ByteBuffer.allocateDirect(vertices.length * 4); buff.order(ByteOrder.nativeOrder()); verticesBuffer = buff.asFloatBuffer(); verticesBuffer.put(vertices); verticesBuffer.position(0); buff = ByteBuffer.allocateDirect(textureVertices.length * 4); buff.order(ByteOrder.nativeOrder()); textureBuffer = buff.asFloatBuffer(); textureBuffer.put(textureVertices); textureBuffer.position(0);}</code>
創建著色器
著色器只不過是簡單的運行在GPU中的每個單獨的頂點的C程序,在本次教程中,我們使用兩種著色器:頂點著色器和片段著色器。
頂點著色器的代碼:
<code class=「hljs」 glsl=「」>attribute vec4 aPosition; attribute vec2 aTexPosition; varying vec2 vTexPosition; void main() { gl_Position = aPosition; vTexPosition = aTexPosition; };</code>
片段著色器的代碼
<code class=「hljs」 glsl=「」>precision mediump float; uniform. sampler2D uTexture; varying vec2 vTexPosition; void main() { gl_FragColor = texture2D(uTexture, vTexPosition); };</code>
如果你了解OpenGL,那麼這段代碼對你來說是熟悉的,如果你不能理解這段代碼,你可以參考OpenGL documentation。
『玖』 頂點和片段著色器 和 表面著色器的區別
對顯卡的性能影響很大,是顯卡的主要參數之一。
什麼是頂點著色器?
1 頂點著色器是一組指令代碼,這組指令代碼在頂點被渲染時執行。
2 同一時間內,只能激活一個頂點著色器。
3 每個源頂點著色器最多擁有128條指令(DirextX8.1),而在DirectX9,則可以達到256條。
為什麼大家要使用頂點著色器?
1 頂點著色器可以提高渲染場景速度。
2 用頂點著色器你可以做布類模擬,高級別動畫,實時修改透視效果(比如水底效果),高級光亮(需要像素著色器支持)
頂點著色器如何運作?
簡單說來,運作方式如下:當渲染一個頂點時,API會執行你在頂點著色器中所寫的指令。依靠這種方法,你可以自己控制每個頂點,包括渲染,確定位置,是否顯示在屏幕上。
如何創建一個頂點著色器?
用一個文本編輯器就可以了!我建議你們使用notepad或者vs開發環境來創建和修改著色器。另外,必須擁有一個支持可編程著色器的顯卡。寫完著色器後,保存他。API就可以調用他了(Direct3D或OpenGL)。API通過一些函數來調用這些代碼指令到硬體中。
什麼是像素著色器?
1 像素著色器也是一組指令,這組指令在頂點中像素被渲染時執行。在每個執行時間,都會有很多像素被渲染。(像素的數目依靠屏幕的解析度決定)
2像素著色器的指令和頂點著色器的指令非常接近。像素著色器不能像頂點著色器那樣,單獨存在。他們在運行的時候,必須有一個頂點著色器被激活。
為什麼大家要使用像素著色器?
1 像素著色器過去是一種高級圖形技術,專門用來提高渲染速度。
2 和頂點著色器一樣,使用像素著色器,程序員能自定義渲染每個像素。
像素著色器如何運作?
一個像素著色器操作頂點上單獨的像素。和頂點著色器一樣,像素著色器源代碼也是通過一些API載入到硬體的。
如何創建一個像素著色器?
也和頂點著色器一樣,你只需要一個文本編輯器和支持著色器編程的顯卡即可。同樣,API(Direct3D OpenGL)載入像素著色器代碼指令到硬體中。
『拾』 誰用過GLSL的Attribute類型
導入模型時,就把Tangent和Binormal計算並記錄了下來,然後用多重紋理傳至vs。
傳下代碼:
1.求三角面三個頂點各自的T和B
//計算切線
void CModel::calculateTangent()
{
vector<CTangentCoord> tangentCoordList;
vector<CBinormalCoord> binormalCoordList;
CTangentCoord newTangentCoord;
CBinormalCoord newBinormalCoord;
CVertexCoord v[3], P, Q;
CTextureCoord uv[3];
float s1, t1;
float s2, t2;
float tmp;
int i, j, k, p;
//計算切線
for (i=1;i<numOfFace;i++)
{
for (j=0;j<3;j++)
{
//提取到對應位置
for (p=j;p<3+j;p++)
{
k = pFaceList[i].vertexCoordIndex[p%3];
v[p%3].x = pVertexCoordList[k].x;
v[p%3].y = pVertexCoordList[k].y;
v[p%3].z = pVertexCoordList[k].z;
k = pFaceList[i].textureCoordIndex[p%3];
uv[p%3].x = pTextureCoordList[k].x;
uv[p%3].y = pTextureCoordList[k].y;
}
P.x = v[1].x - v[0].x;
P.y = v[1].y - v[0].y;
P.z = v[1].z - v[0].z;
Q.x = v[2].x - v[0].x;
Q.y = v[2].y - v[0].y;
Q.z = v[2].z - v[0].z;
s1 = uv[1].x - uv[0].x;
t1 = uv[1].y - uv[0].y;
s2 = uv[2].x - uv[0].x;
t2 = uv[2].y - uv[0].y;
if (fabs(s1*t2 - s2*t1) == 0.0)
{
tmp = 1.0;
}
else
{
tmp = 1.0 / (s1*t2 - s2*t1);
}
newTangentCoord.x = (t2*P.x - t1*Q.x) * tmp;
newTangentCoord.y = (t2*P.y - t1*Q.y) * tmp;
newTangentCoord.z = (t2*P.z - t1*Q.z) * tmp;
newBinormalCoord.x = (s1*Q.x - s2*P.x) * tmp;
newBinormalCoord.y = (s1*Q.y - s2*P.y) * tmp;
newBinormalCoord.z = (s1*Q.z - s2*P.z) * tmp;
tangentCoordList.push_back(newTangentCoord);
binormalCoordList.push_back(newBinormalCoord);
}
}
//拷貝
pTangentCoordList = new CTangentCoord[numOfFace * 3];
pBinormalCoordList = new CBinormalCoord[numOfFace * 3];
for (i=0;i<numOfFace*3;i++)
{
pTangentCoordList[i].x = tangentCoordList[i].x;
pTangentCoordList[i].y = tangentCoordList[i].y;
pTangentCoordList[i].z = tangentCoordList[i].z;
pBinormalCoordList[i].x = binormalCoordList[i].x;
pBinormalCoordList[i].y = binormalCoordList[i].y;
pBinormalCoordList[i].z = binormalCoordList[i].z;
}
}
2.導入shader
//給出法向量
k = normalIndex;
glNormal3f(pModel->pNormalCoordList[k].x, pModel->pNormalCoordList[k].y, pModel->pNormalCoordList[k].z);
//紋理坐標
k = textureIndex;
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, pModel->pTextureCoordList[k].x, pModel->pTextureCoordList[k].y);
//頂點坐標
k = vertexIndex;
glVertex3f(pModel->pVertexCoordList[k].x, pModel->pVertexCoordList[k].y, pModel->pVertexCoordList[k].z);
//切線(用多重紋理)
glMultiTexCoord3fARB(GL_TEXTURE1_ARB, pModel->pTangentCoordList[i+j].x,
pModel->pTangentCoordList[i+j].y,
pModel->pTangentCoordList[i+j].z);
//次法線
glMultiTexCoord3fARB(GL_TEXTURE2_ARB, pModel->pBinormalCoordList[i+j].x,
pModel->pBinormalCoordList[i+j].y,
pModel->pBinormalCoordList[i+j].z);
3.vs代碼
#version 120
varying mat3 toObjectLocal;
varying vec4 textureCoord;
varying mat3 normalMatrix;
varying vec3 positionData;
varying vec3 normalData;
varying vec4 colorData;
varying vec4 specularData;
void main()
{
//位置
vec4 position = gl_ModelViewMatrix * gl_Vertex;
positionData = vec3(position) / position.w;
//漫射顏色
colorData = gl_FrontMaterial.diffuse;
//鏡面反射
specularData = gl_FrontMaterial.specular;
//法線
normalData = normalize(gl_NormalMatrix * gl_Normal);
textureCoord = gl_MultiTexCoord0;
normalMatrix = gl_NormalMatrix;
gl_Position = ftransform();
//切線空間變換矩陣
vec3 tangent = gl_MultiTexCoord1.stp;
vec3 binormal = gl_MultiTexCoord2.stp;
vec3 T = normalize(tangent);
vec3 B = normalize(binormal);
vec3 N = normalize(gl_Normal);
toObjectLocal = mat3(
T.x, B.x, N.x,
T.y, B.y, N.y,
T.z, B.z, N.z);
toObjectLocal = transpose(toObjectLocal);//轉置原因見fs代碼
}
4.fs代碼(我用的是延時渲染)
#version 330
uniform sampler2D mapKd;
uniform sampler2D mapKs;
uniform sampler2D mapNormal;
uniform int shadeModelType;
layout (location = 0) out vec3 PositionData;
layout (location = 1) out vec3 NormalData;
layout (location = 2) out vec4 ColorData;
varying mat3 toObjectLocal;
varying vec4 textureCoord;
varying mat3 normalMatrix;
varying vec3 positionData;
varying vec3 normalData;
varying vec4 colorData;
varying vec4 specularData;
void main()
{
int useTexture[3];
int p = shadeModelType, i = 0;
//是否使用三種紋理
while (i < 3)
{
useTexture[i] = p % 2;
p = p / 2;
i++;
}
PositionData = positionData;
if (useTexture[0] == 1)
ColorData = texture2D(mapKd, textureCoord.xy);
else
ColorData = colorData;
if (useTexture[1] == 1)
{
vec4 specular = texture2D(mapKs, textureCoord.xy);
ColorData.w = 0.27 * specular.x + 0.67 * specular.y + 0.06 * specular.z;
}
else
ColorData.w = 0.27 * specularData.x + 0.67 * specularData.y + 0.06 * specularData.z;
if (useTexture[2] == 1)
{
vec3 objectNormal = texture2D(mapNormal, textureCoord.xy).stp;
objectNormal = (objectNormal - 0.5) * 2.0;
NormalData = normalize(normalMatrix * toObjectLocal * objectNormal);//轉置的TBN矩陣和法線貼圖中的法線相乘,得出物體坐標系下的法線,再乘以gl_NormalMatrix轉至視圖空間
}
else
NormalData = normalData;
}