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的第一个例子,砖墙。顶点着色器如下:
//一致变量 设置灯光位置(提供眼睛坐标位置)
uniform vec3 LightPosition;
//常量 镜面反射强度
const float SpecularContribution = 0.3;
//常量 漫反射强度
const float DiffuseContribution = 1.0 - SpecularContribution;
//物体顶点上的光强度 易变变量 传给片断着色器
varying float LightIntensity;
//物体顶点位置
varying vec2 MCposition;
//顶点着色器入口
void main(void)
{
//顶点在视图坐标中的位置
vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);
//顶点在视图坐标中的法向量,gl_NormalMatrix为gl_ModelViewMatrix逆矩阵的倒置矩阵
vec3 tnorm = normalize(gl_NormalMatrix * gl_Normal);
//视图坐标中灯光的位置 取顶点到灯光的单位向量
//LightPosition如果是全局坐标里的值,应该进行gl_ModelViewMatrix转换
vec3 lightVec = normalize(LightPosition - ecPosition);
//灯光到顶点经过tnorm的表面反射光向量 reflect i n = i - 2.0*dot(N,I)*N
vec3 reflectVec = reflect(-lightVec,tnorm);
//查看位置向量单位向量 就是在视图坐标中,眼睛到顶点的向量.eye(0,0,0)
vec3 viewVec = normalize(-ecPosition); //一是用来计算lightVec的tnorm角度,二是计算lightVec在顶点上的漫反射强度
float diffuse = max(dot(lightVec,tnorm),0.0);
float spec = 0.0;
//只有lightVec与tnorm的角度在90度之间,才可能反射光照
if(diffuse > 0.0)
{
//计算反射光在人看的方向上的分量。角度越少,分量越多。
spec = max(dot(reflectVec,viewVec),0.0);
spec = pow(spec,16.0);
}
//计算光照,主要成分为漫反射与镜面反射
LightIntensity = DiffuseContribution * diffuse + SpecularContribution * spec;
//传个片断着色器的x,y位置
MCposition = gl_Vertex.xy;
//定点坐标位置不变性
gl_Position = ftransform();
}
第一个例子,我注释还是写的很满的,这里说下其中reflect函数的算法:
给出橙皮书里的图片,增加大家的理解:
在顶点着色器上,我们可以看到光照的计算是怎么样的,可以看到,漫反射只和灯光与物体的角度与强度有关,而镜面反射不仅和灯光与物体角度有关,还有人与反射灯光的位置有关。
在片断着色器上,我们可以看到对许多OpenGL内置函数的运用。
uniform vec3 BrickColor, MortarColor;
//整个砖(砖与外边)
uniform vec2 BrickSize;
//只包含砖的大小
uniform vec2 BrickPct;
//片断着色器传过来的值
varying vec2 MCposition;
varying vec2 LightIntensity;
void main(void)
{
vec3 color;
vec2 position,useBrick;
//得到在整个砖墙中属于在那块整个砖中
//如结果是(2.2,3.8)表示在第三行中的第二块的右下角((0.2,0.8)在(1.0,1.0)的位置)
position = MCposition / BrickSize;
//单数行比双数行的起点多半块砖
if(fract(position.y * 0.5) > 0.5)
position.x += 0.5;
//得到在本身砖块的详细位置
position = fract(position);
//确认是砖,还是外边
useBrick = step(position,BrickPct);
color = mix(MortarColor,BrickColor,useBrick.x * useBrick.y);
color *= LightIntensity;
gl_FragColor = vec4(color,1.0);
}
整个过程还是很好理解的,position = MCposition / BrickSize;这句,如果postion是(2.2,3.8)表示在第三行中的第二块的右下角((0.2,0.8)在(1.0,1.0)的位置),可以看到他整数位与小数位分别表示不同的意思。
最后,我们要运用我们的着色器代码,以及完成相关参数传入:
type GLSLCommon()=
static member CreateShadersForString(shaderType:ShaderType,source:string)=
let shaderObject = GL.CreateShader(shaderType)
GL.ShaderSource(shaderObject,source)
GL.CompileShader(shaderObject)
let log = GL.GetShaderInfoLog(shaderObject)
let status = GL.GetShader(shaderObject,ShaderParameter.CompileStatus)
shaderObject,status//,log
static member CreateShadersForFile(shaderType:ShaderType,fileName:string)=
if not (File.Exists(fileName)) then failwith "not find file!"
let fs = new StreamReader(fileName)
let source = fs.ReadToEnd()
fs.Close()
let result = GLSLCommon.CreateShadersForString(shaderType,source)
result
static member CreateShaders(shaderType:ShaderType,code:string) =
let result = if File.Exists(code) then GLSLCommon.CreateShadersForFile(shaderType,code) else GLSLCommon.CreateShadersForString(shaderType,code)
result
static member CreateProgram([<ParamArray>]shader:(int*int) []) =
let program = GL.CreateProgram()
shader |> Array.iter (fun p -> GL.AttachShader(program,fst p))
GL.LinkProgram(program)
shader |> Array.iter (fun p -> GL.DeleteShader(fst p))
GL.LinkProgram(program)
let log = GL.GetProgramInfoLog(program)
let status = GL.GetProgram(program,ProgramParameter.LinkStatus)
program,status
static member GetUniform(programID:int,name:string) =
GL.GetUniformLocation(programID,name)
static member SetUniform(programID:int,name:string,[<ParamArray>]value:int[]) =
let uniformID = GLSLCommon.GetUniform(programID,name)
if value.Length = then GL.Uniform1(uniformID,value.[])
if value.Length = then GL.Uniform2(uniformID,value.[],value.[])
if value.Length = then GL.Uniform3(uniformID,value.[],value.[],value.[])
if value.Length = then GL.Uniform4(uniformID,value.[],value.[],value.[],value.[])
static member SetUniform(programID:int,name:string,[<ParamArray>]value:System.Single[]) =
let uniformID = GLSLCommon.GetUniform(programID,name)
if value.Length = then GL.Uniform1(uniformID,value.[])
if value.Length = then GL.Uniform2(uniformID,value.[],value.[])
if value.Length = then GL.Uniform3(uniformID,value.[],value.[],value.[])
if value.Length = then GL.Uniform4(uniformID,value.[],value.[],value.[],value.[]) type GLSLProgram()=
let programId = GL.CreateProgram()
member this.ID with get() = programId
member this.LinkSources([<ParamArray>]sources:(ShaderType * string)[]) =
let shader = sources |> Array.map(fun p -> GLSLCommon.CreateShaders(fst p,snd p))
shader |> Array.iter (fun p -> GL.AttachShader(programId,fst p))
GL.LinkProgram(programId)
shader |> Array.iter (fun p -> GL.DeleteShader(fst p))
GL.LinkProgram(programId)
let log = GL.GetProgramInfoLog(programId)
let status = GL.GetProgram(programId,ProgramParameter.LinkStatus)
status,log
member this.GetUniform(name:string)=
GLSLCommon.GetUniform(programId,name)
member this.SetUniform(name:string,[<ParamArray>]values:int[]) =
GLSLCommon.SetUniform(programId,name,values)
member this.SetUniform(name:string,[<ParamArray>]values:System.Single[]) =
GLSLCommon.SetUniform(programId,name,values) let result = program.LinkSources((ShaderType.VertexShader,"Data/Shaders/Brick_VS.glsl"),(ShaderType.FragmentShader,"Data/Shaders/Brick_FS.glsl")) override v.OnRenderFrame e =
base.OnRenderFrame e
GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
GL.UseProgram(program.ID)
let mutable lookat = Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadMatrix(&lookat)
program.SetUniform("LightPosition",.f,.f,-.f)
program.SetUniform("BrickColor",1.0f,0.3f,0.2f)
program.SetUniform("MortarColor",0.85f,0.86f,0.84f)
program.SetUniform("BrickSize",1.2f,.f)
program.SetUniform("BrickPct",0.9f,0.85f)
GL.Normal3(-Vector3.UnitZ)
GL.Begin(BeginMode.Quads)
GL.Vertex3(-6.6f, -6.4f,.f)
GL.Vertex3(6.6f, -6.4f,.f)
GL.Vertex3(6.6f, 6.4f,.f)
GL.Vertex3(-6.6f, 6.4f,.f)
GL.End();
v.SwapBuffers()
相关链接与编译的代码我整理了下,方便以后调用。整个过程没什么好说的,可以看到处理很简单,没有打开光照啥的,但是相应处理全是用我们写的着色器。我们来看下效果图:
注意在OpenTK中,相关着色器源码不能出现中文注释,否则他不能申请正确的内存空间。
GLSL着色语言学习。橙皮书第一个例子GLSL+OpenTK+F#的实现。的更多相关文章
- OpenGL12-shader(GLSL)着色语言1(代码已上传)
OpenGL着色语言(GLSL――OpenGL Shading Language)是用来在OpenGL中着色编程的语言, 也即开发人员写的短小的自定义程序,他们是在图形卡的GPU (Graphic P ...
- OpenGL12-shader(GLSL)着色语言2-(参数传递)(代码以上传)
上一篇中介绍了如何使用shader,用来一个最简单的shader,计算顶点的位置,调用了 OpenGL 顶点着色语言中的内置变量对顶点进行操作,这一例程中,将展示如何将应用层 的数据传递到shader ...
- OpenGL12-shader(GLSL)着色语言3-(属性参数)(代码已上传)
上一个例程中,使用了uniform 类型的变量,uniform可以理解为全局变量,这一节中使用 的是attribute类型的变量,翻译过来就是属性,他是与顶点绑定的,就意味着一个顶点可以 有很多个属性 ...
- Lisp语言学习的书
Scheme <How to Design Programs : An Introduction to Programming and Computing>(<程序设计方法>) ...
- OpenGL12-shader(GLSL)着色语言4-广告版的实现
之前介绍了vertex shader的基本原理,现在我们来做一个简单的实践,在游戏中广告版(布告版) 随处可见,而且效率很高,现在我们就使用shader来实现这一过程,首先我们要知道布告版的原理 实际 ...
- OpenGL红宝书第一个例子:绘制两个三角形
1. 环境配置 在这里不在做环境配置的说明,因为网上可以找到很多类似的教程,如果有需要可以@我,我也希望能帮到大家,其它的不说了,先上我的代码 2. 第一个程序代码 创建LoadShader.h #p ...
- python学习中的第一个例子
搭建python 先学习下当小白鼠 1 看下自己的python版本 python -v 2 然后,用pip安装开发Web App需要的第三方库: 异步框架aiohttp: pip3 install a ...
- 《C语言入门1.2.3—一个老鸟的C语言学习心得》—清华大学出版社炮制的又一本劣书及伪书
<C语言入门1.2.3—一个老鸟的C语言学习心得>—清华大学出版社炮制的又一本劣书及伪书 [薛非评] 区区15页,有80多个错误. 最严重的有: 通篇完全是C++代码,根本不是C语言代码. ...
- win7,64bit下的OpenGL着色语言(glsl)开发环境配置(原)
一.环境准备: 系统环境win7,64位,双显卡:集成显卡+gt540m,gt540建议下载最新的驱动,可以支持到opengl4.3标准,一般双显的笔记本,程序默认启用的是集显,我机器的集显驱动有点老 ...
随机推荐
- Egret入门了解
0.前言 这个星期没有什么事做,就想找点技术了解一下.前段时间看过Egret,用来开发HTML5小游戏.一开始以为很麻烦的,但是经过这两天了解了一下,如果用这个游戏引擎来开发一些简单的游戏,还是蛮方便 ...
- ssh以密钥的方式登录服务器时,只要有密钥可以登服务器,如果有密钥和公钥同时存在(在公钥没问题的情况下可以),但如果公钥有问题,就不能登录成功
在~/.ssh/下如果只有密钥或公私同时存在时,都可以成功登录服务器,但!!!!!!如果公钥有换成别的服务器的公钥时,是无法登录远程的服务器!!!!
- An introduction to High Availability Architecture
https://www.getfilecloud.com/blog/an-introduction-to-high-availability-architecture/ An introduction ...
- Java中使用Oracle的客户端 load data和sqlldr命令执行数据导入到数据库中
Windows环境下测试代码: import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundExcep ...
- [Windows Azure] How to Monitor Cloud Services
How to Monitor Cloud Services To use this feature and other new Windows Azure capabilities, sign up ...
- App store 应用审核由于 IPv6 网络问题被拒的一点分析
App store 应用审核由于 IPv6 网络问题被拒的一点分析 六月以后陆续有一些软件提交市场的时候被拒了,症状基本就是无法登陆啥的.我们公司的应用也未能幸免. 很多同学也想了不少办法,申诉. ...
- 【编码】Base64编码
简述 为什么叫Base64?个人理解是,基础的64个字符. 而它的作用?用基础的(可理解为可安全传输的)64个字符,来表示难以表示的二进制或对程序造成干扰的字符. Base64的编码过程 自行编码分析 ...
- Office_Visio_Pro_2007
Office_Visio_Pro_2007http://pan.baidu.com/share/link?shareid=473782&uk=3474501992 解压后在文件夹里找到密钥[一 ...
- iis下php 500错误
很不想用iis,然而客户不想增加机器,只好按客户的意思了.可是没想到发送短信以在本地 机器上是好的,在iis下直接500. 一开始以为是防火墙问题,后来检查了一下没有,再后来换了一个短信接口, ...
- WIN7或者WIN8上边框的异常问题的解决攻略
//主要两个步骤://第一个步骤就是在CMainFrame::OnCreate里面增加 HINSTANCE hInst = LoadLibrary(_T("UxTheme.dll" ...