zoukankan      html  css  js  c++  java
  • 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中,相关着色器源码不能出现中文注释,否则他不能申请正确的内存空间。

    最后给出相应的源码:可执行文件 源代码

  • 相关阅读:
    mysql启动失败
    mini.open参数传递
    json的key动态赋值
    Python文件格式 .py .pyc .pyw .pyo .pyd的主要区别
    centos 7.2 安装mongodb 3.4.4免编译
    tomcat 8.0安装ssl证书,及centos7.2 的openssl升级到最新版本,及ERR_SSL_OBSOLETE_CIPHER错误解决
    云服务器 Centos7.0 部署
    笔记
    oracle 视图的创建,游标,left join
    在Extjs中对日期的处理,以及在后端数据在SQL语句的判断处理
  • 原文地址:https://www.cnblogs.com/zhouxin/p/3460690.html
Copyright © 2011-2022 走看看