GLSL着色語言學習。橙皮書第一個例子GLSL+OpenTK+F#的實現。


  Opengl紅皮書有選擇的看了一些,最后的講着色語言GLSL的部分看的甚為不理解,然后找到Opengl橙皮書,然后就容易理解多了。 

  在前面,我們或多或少接觸到Opengl的處理過程,只說前面一些處理,簡單來說:頂點操作-組裝圖形-柵欄化-片斷處理-幀緩沖。其中頂點操作相當於我們在程序里設定頂點,法向量等,組裝圖形就是opengl以我們設定的格式連接頂點,如組裝成三角形,四邊形等。柵欄化就是把上部操作的圖元分解成更小的單元,如一個三角形里有100個像素,在這個過程會轉換成100個片斷,這個片斷包含了窗口坐標,深度,顏色,紋理坐標等組成。片斷處理就是把上面生成的片斷進行一些如組合紋理,霧效果等。

  在最開始,Opengl在上面的各個處理被設置好,如前面,我們按照給定的API來打開光照,設定紋理,設定材質等,這個在大部分情況下已經能得到比較好的效果,但是通過Opengl API,我們不能更改Opengl圖形管道的一些基本操作,也不能改變相應的順序,如果想要實現一些特殊的效果,可能就實現不了了。GLSL就是在這種情況下出現的,主要是允許應用程序對在Opengl處理流程進行自己的實現。

  GLSL讓我現在的理解,就是語法簡單,用起來就需要經驗了,為什么這么說,因為GLSL的語法是在C和C++的基礎了簡化了一些元素與特性,如GLSL里沒有指針,字符串以及相應操作,不支持double,byte,short,long以及相應的符號形式。以及聯合,枚舉。大家可以想象一下,還有什么在里面,但是用起來一點都不簡單,就我現在的感覺,里面內置的函數,相關變量,常量不少,靈活運用肯定需要一定的GLSL代碼量。最后GLSL為了突出圖形計算這塊,內置了一些圖形計算所需要的結構vec3,vec4,mat4等,最后GLSL和F#一樣,不支持數據類型自動提升,如float f = 1這個是錯的,應該是 f = 1.0.

  GLSL通常會包含二種着色器,頂點着色器和片斷着色器,最常見用法是在頂點着色器里生成所需要的值,然后傳給片斷着色器用。着色器中常用限定符有attribute,unifrom,varying,const.其中attribute是應用程序傳給頂點着色器用的,着色器不能修改。unifrom一般是應用程序用於設定頂點着色器和片斷着色器相關初始化值。varying用於傳遞頂點着色器的值給片斷着色器。const和C++里差不多,定義不可變常量。

  GLSL內置的相關變量,常量,結構大家在OpenGL橙皮書里找。下面我們來完整的實現OpenGL的第一個例子,磚牆。頂點着色器如下:

 1 //一致變量 設置燈光位置(提供眼睛坐標位置)
 2 uniform vec3 LightPosition;
 3 //常量 鏡面反射強度
 4 const float SpecularContribution = 0.3;
 5 //常量 漫反射強度
 6 const float DiffuseContribution = 1.0 - SpecularContribution;
 7 //物體頂點上的光強度 易變變量 傳給片斷着色器
 8 varying float LightIntensity;
 9 //物體頂點位置
10 varying vec2 MCposition;
11 //頂點着色器入口
12 void main(void)
13 {
14     //頂點在視圖坐標中的位置
15     vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);
16     //頂點在視圖坐標中的法向量,gl_NormalMatrix為gl_ModelViewMatrix逆矩陣的倒置矩陣
17     vec3 tnorm = normalize(gl_NormalMatrix * gl_Normal);
18     //視圖坐標中燈光的位置 取頂點到燈光的單位向量
19     //LightPosition如果是全局坐標里的值,應該進行gl_ModelViewMatrix轉換
20     vec3 lightVec = normalize(LightPosition - ecPosition);
21     //燈光到頂點經過tnorm的表面反射光向量 reflect i n = i - 2.0*dot(N,I)*N
22     vec3 reflectVec = reflect(-lightVec,tnorm);
23     //查看位置向量單位向量 就是在視圖坐標中,眼睛到頂點的向量.eye(0,0,0)
24     vec3 viewVec = normalize(-ecPosition);
25 
26     //一是用來計算lightVec的tnorm角度,二是計算lightVec在頂點上的漫反射強度
27     float diffuse = max(dot(lightVec,tnorm),0.0);
28     float spec = 0.0;
29     //只有lightVec與tnorm的角度在90度之間,才可能反射光照
30     if(diffuse > 0.0)
31     {        
32         //計算反射光在人看的方向上的分量。角度越少,分量越多。
33         spec = max(dot(reflectVec,viewVec),0.0);
34         spec = pow(spec,16.0);
35     }
36     //計算光照,主要成分為漫反射與鏡面反射
37     LightIntensity = DiffuseContribution * diffuse + SpecularContribution * spec;
38     //傳個片斷着色器的x,y位置
39     MCposition = gl_Vertex.xy;
40     //定點坐標位置不變性
41     gl_Position = ftransform();
42 }
View Code

第一個例子,我注釋還是寫的很滿的,這里說下其中reflect函數的算法:

給出橙皮書里的圖片,增加大家的理解:

在頂點着色器上,我們可以看到光照的計算是怎么樣的,可以看到,漫反射只和燈光與物體的角度與強度有關,而鏡面反射不僅和燈光與物體角度有關,還有人與反射燈光的位置有關。

在片斷着色器上,我們可以看到對許多OpenGL內置函數的運用。 

 1 uniform vec3 BrickColor, MortarColor;
 2 //整個磚(磚與外邊)
 3 uniform vec2 BrickSize;
 4 //只包含磚的大小
 5 uniform vec2 BrickPct;
 6 //片斷着色器傳過來的值
 7 varying vec2 MCposition;
 8 varying vec2 LightIntensity;
 9 void main(void)
10 {
11     vec3 color;
12     vec2 position,useBrick;
13     //得到在整個磚牆中屬於在那塊整個磚中
14     //如結果是(2.2,3.8)表示在第三行中的第二塊的右下角((0.2,0.8)在(1.0,1.0)的位置)
15     position = MCposition / BrickSize;
16     //單數行比雙數行的起點多半塊磚
17     if(fract(position.y * 0.5) > 0.5)
18         position.x += 0.5;
19     //得到在本身磚塊的詳細位置
20     position = fract(position);
21     //確認是磚,還是外邊
22     useBrick = step(position,BrickPct);
23     color = mix(MortarColor,BrickColor,useBrick.x * useBrick.y);
24     color *= LightIntensity;
25     gl_FragColor = vec4(color,1.0);
26 }
View Code  

整個過程還是很好理解的,position = MCposition / BrickSize;這句,如果postion是(2.2,3.8)表示在第三行中的第二塊的右下角((0.2,0.8)在(1.0,1.0)的位置),可以看到他整數位與小數位分別表示不同的意思。

最后,我們要運用我們的着色器代碼,以及完成相關參數傳入:

 1 type GLSLCommon()=
 2     static member CreateShadersForString(shaderType:ShaderType,source:string)=
 3         let shaderObject = GL.CreateShader(shaderType)
 4         GL.ShaderSource(shaderObject,source)
 5         GL.CompileShader(shaderObject)
 6         let log = GL.GetShaderInfoLog(shaderObject)
 7         let status = GL.GetShader(shaderObject,ShaderParameter.CompileStatus)
 8         shaderObject,status//,log
 9     static member CreateShadersForFile(shaderType:ShaderType,fileName:string)=   
10         if not (File.Exists(fileName)) then failwith "not find file!"     
11         let fs = new StreamReader(fileName)
12         let source = fs.ReadToEnd()
13         fs.Close()
14         let result = GLSLCommon.CreateShadersForString(shaderType,source)
15         result
16     static member CreateShaders(shaderType:ShaderType,code:string) =
17         let result = if File.Exists(code) then GLSLCommon.CreateShadersForFile(shaderType,code) else GLSLCommon.CreateShadersForString(shaderType,code) 
18         result
19     static member CreateProgram([<ParamArray>]shader:(int*int) []) =
20         let program = GL.CreateProgram()
21         shader |> Array.iter (fun p -> GL.AttachShader(program,fst p))
22         GL.LinkProgram(program)
23         shader |> Array.iter (fun p -> GL.DeleteShader(fst p))
24         GL.LinkProgram(program)
25         let log = GL.GetProgramInfoLog(program)
26         let status = GL.GetProgram(program,ProgramParameter.LinkStatus)
27         program,status
28     static member GetUniform(programID:int,name:string) =
29         GL.GetUniformLocation(programID,name)
30     static member SetUniform(programID:int,name:string,[<ParamArray>]value:int[]) =
31         let uniformID = GLSLCommon.GetUniform(programID,name)
32         if value.Length = 1 then GL.Uniform1(uniformID,value.[0])
33         if value.Length = 2 then GL.Uniform2(uniformID,value.[0],value.[1])
34         if value.Length = 3 then GL.Uniform3(uniformID,value.[0],value.[1],value.[2])
35         if value.Length = 4 then GL.Uniform4(uniformID,value.[0],value.[1],value.[2],value.[3])
36     static member SetUniform(programID:int,name:string,[<ParamArray>]value:System.Single[]) =
37         let uniformID = GLSLCommon.GetUniform(programID,name)
38         if value.Length = 1 then GL.Uniform1(uniformID,value.[0])
39         if value.Length = 2 then GL.Uniform2(uniformID,value.[0],value.[1])
40         if value.Length = 3 then GL.Uniform3(uniformID,value.[0],value.[1],value.[2])
41         if value.Length = 4 then GL.Uniform4(uniformID,value.[0],value.[1],value.[2],value.[3])
42 
43 type GLSLProgram()=
44     let programId = GL.CreateProgram()
45     member this.ID with get() = programId
46     member this.LinkSources([<ParamArray>]sources:(ShaderType * string)[]) =
47         let shader = sources |> Array.map(fun p -> GLSLCommon.CreateShaders(fst p,snd p))
48         shader |> Array.iter (fun p -> GL.AttachShader(programId,fst p))
49         GL.LinkProgram(programId)
50         shader |> Array.iter (fun p -> GL.DeleteShader(fst p))
51         GL.LinkProgram(programId)
52         let log = GL.GetProgramInfoLog(programId)
53         let status = GL.GetProgram(programId,ProgramParameter.LinkStatus)
54         status,log
55     member this.GetUniform(name:string)=
56         GLSLCommon.GetUniform(programId,name)
57     member this.SetUniform(name:string,[<ParamArray>]values:int[]) =
58         GLSLCommon.SetUniform(programId,name,values)
59      member this.SetUniform(name:string,[<ParamArray>]values:System.Single[]) =
60         GLSLCommon.SetUniform(programId,name,values) 
61   
62     let result = program.LinkSources((ShaderType.VertexShader,"Data/Shaders/Brick_VS.glsl"),(ShaderType.FragmentShader,"Data/Shaders/Brick_FS.glsl"))
63 
64     override v.OnRenderFrame e =
65         base.OnRenderFrame e
66         GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
67         GL.UseProgram(program.ID)        
68         let mutable lookat = Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)
69         GL.MatrixMode(MatrixMode.Modelview)
70         GL.LoadMatrix(&lookat)
71         program.SetUniform("LightPosition",0.f,0.f,-5.f)
72         program.SetUniform("BrickColor",1.0f,0.3f,0.2f)
73         program.SetUniform("MortarColor",0.85f,0.86f,0.84f)
74         program.SetUniform("BrickSize",1.2f,1.f)
75         program.SetUniform("BrickPct",0.9f,0.85f)
76         GL.Normal3(-Vector3.UnitZ)
77         GL.Begin(BeginMode.Quads)       
78         GL.Vertex3(-6.6f, -6.4f,1.f)        
79         GL.Vertex3(6.6f, -6.4f,1.f)
80         GL.Vertex3(6.6f, 6.4f,1.f)         
81         GL.Vertex3(-6.6f, 6.4f,1.f)
82         GL.End();
83         v.SwapBuffers()
View Code

相關鏈接與編譯的代碼我整理了下,方便以后調用。整個過程沒什么好說的,可以看到處理很簡單,沒有打開光照啥的,但是相應處理全是用我們寫的着色器。我們來看下效果圖:

注意在OpenTK中,相關着色器源碼不能出現中文注釋,否則他不能申請正確的內存空間。

最后給出相應的源碼:可執行文件 源代碼

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM