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中,相关着色器源码不能出现中文注释,否则他不能申请正确的内存空间。
最后给出相应的源码:可执行文件 源代码
转载于:https://www.cnblogs.com/zhouxin/p/3460690.html
GLSL着色语言学习。橙皮书第一个例子GLSL+OpenTK+F#的实现。相关推荐
- Modern OpenGL - GLSL着色语言2:GLSL入口函数和GLSL中的变量
文章目录 1 GLSL的入口函数和基本结构 2 GLSL中的变量声明 3 GLSL中变量的作用域 4 GLSL中变量的初始化 5 GLSL中变量的隐式转换 1 GLSL的入口函数和基本结构 对于很多编 ...
- C语言学习知识总结————第一节课
一.为什么学习C语言? C生万物 编程之本 长远IT职业的发展的首选 学会了C,就相当掌握了技术核心 C语言是母体语言,是人机交互接近底层的桥梁 C语言与其他主流编程语言的基本结构近乎一至 在最流行的 ...
- Modern OpenGL - GLSL着色语言3:GLSL中的数据类型
文章目录 1 GLSL的基本数据类型 2 GLSL的聚合类型:向量和矩阵 2.1 向量 2.1.1 向量初始化 2.1.1.1 向量初始化 2.1.1.2 向量构造函数的截短 2.1.1.3 向量构造 ...
- 用c语言运行程序的优点,C语言学习与总结---第一章:C语言概述
第一章:C语言概述 1.绪论 2.计算机程序 3.计算机语言 4.C语言的发展及其特点 5.最简单的C语言程序 6.运行C语言程序的方法与步骤 7.程序设计任务 1.绪论 C语言是计算机基础语言,本次 ...
- Opengl红宝书第一个例子_VS2017_glew2.1_x64_记录成功的代码段
2019独角兽企业重金招聘Python工程师标准>>> 忙了一下午, 终于能运行: Opengl编程指南里, 书里代码不全, 好不容量才在网上找到原代码 https://blog.c ...
- 《OpenGL编程指南(原书第9版)》——2.3 OpenGL着色语言概述
2.3 OpenGL着色语言概述 本节将会对OpenGL中着色语言的使用进行一个概述.GLSL具备了C++和Java的很多特性,它也被OpenGL所有阶段中使用的着色器所支持,尽管不同类型的着色器也会 ...
- GLSL ES 语言—矢量矩阵运算
变量如下定义: vec3 v3a, v3b, v3c; float t; 矢量和浮点数的运算 v3b = v3a + f; 其效果是为矢量的每一个分量加上浮点数f,与下面代码相同: v3b.x = v ...
- 语言学习游戏的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
报告页数: 150 图表数: 100 报告价格:¥16800 本文研究全球与中国市场语言学习游戏的发展现状及未来发展趋势,分别从生产和消费的角度分析语言学习游戏的主要生产地区.主要消费地区以及主要的生 ...
- 某叶C语言学习上重大的一步——一元三次方程求解
目前某叶编的最难的程序了......感觉算是跨越吧,之前最难的是一元二次方程求解,虽然是最"难"的,只是因为最长,但是写起来还是很轻松的 不过一元三次方程可一点都不轻松,很累,因为 ...
最新文章
- Gemfile.lock应该包含在.gitignore中吗?
- apache公司主要是做什么的
- 追加内容到文件末尾的几种常用方法
- Effective java笔记3--类和接口1
- java 固定listview_listview Button始终放在底部示例
- [vue] 你知道nextTick的原理吗?
- 如何写一个不带BOM的UTF8文件
- 软硬件融合新时代——让软件够灵活,硬件够高效,鱼和熊掌可兼得
- 数学建模姜启源5.1人口增长——学习记录
- python webkit内核_GitHub - yunate/wke: 基于Webkit精简的纯C接口的浏览器内核,可用于桌面UI、浏览器。...
- foobar 更换皮肤
- 数据挖掘(一)A-Priori
- 不管几岁,少女心万岁~女神节快乐!
- 《中国民生银行天眼日志平台架构演进的平凡之路》阅读有感
- php小蛋白配方,卤蛋多种详细制作配方
- MYSQL 性能优化 index 函数,隐藏,前缀,hash 索引 使用方法(2)
- 理解Memcached缓存[转载]
- OpenStack基金会项目Airship的新成员要做什么?
- 【daisy-framework】MySQL 开发规范
- 零售业10年蜕变,未来的路又在何方?