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#的实现。的更多相关文章

  1. OpenGL12-shader(GLSL)着色语言1(代码已上传)

    OpenGL着色语言(GLSL――OpenGL Shading Language)是用来在OpenGL中着色编程的语言, 也即开发人员写的短小的自定义程序,他们是在图形卡的GPU (Graphic P ...

  2. OpenGL12-shader(GLSL)着色语言2-(参数传递)(代码以上传)

    上一篇中介绍了如何使用shader,用来一个最简单的shader,计算顶点的位置,调用了 OpenGL 顶点着色语言中的内置变量对顶点进行操作,这一例程中,将展示如何将应用层 的数据传递到shader ...

  3. OpenGL12-shader(GLSL)着色语言3-(属性参数)(代码已上传)

    上一个例程中,使用了uniform 类型的变量,uniform可以理解为全局变量,这一节中使用 的是attribute类型的变量,翻译过来就是属性,他是与顶点绑定的,就意味着一个顶点可以 有很多个属性 ...

  4. Lisp语言学习的书

    Scheme <How to Design Programs : An Introduction to Programming and Computing>(<程序设计方法>) ...

  5. OpenGL12-shader(GLSL)着色语言4-广告版的实现

    之前介绍了vertex shader的基本原理,现在我们来做一个简单的实践,在游戏中广告版(布告版) 随处可见,而且效率很高,现在我们就使用shader来实现这一过程,首先我们要知道布告版的原理 实际 ...

  6. OpenGL红宝书第一个例子:绘制两个三角形

    1. 环境配置 在这里不在做环境配置的说明,因为网上可以找到很多类似的教程,如果有需要可以@我,我也希望能帮到大家,其它的不说了,先上我的代码 2. 第一个程序代码 创建LoadShader.h #p ...

  7. python学习中的第一个例子

    搭建python 先学习下当小白鼠 1 看下自己的python版本 python -v 2 然后,用pip安装开发Web App需要的第三方库: 异步框架aiohttp: pip3 install a ...

  8. 《C语言入门1.2.3—一个老鸟的C语言学习心得》—清华大学出版社炮制的又一本劣书及伪书

    <C语言入门1.2.3—一个老鸟的C语言学习心得>—清华大学出版社炮制的又一本劣书及伪书 [薛非评] 区区15页,有80多个错误. 最严重的有: 通篇完全是C++代码,根本不是C语言代码. ...

  9. win7,64bit下的OpenGL着色语言(glsl)开发环境配置(原)

    一.环境准备: 系统环境win7,64位,双显卡:集成显卡+gt540m,gt540建议下载最新的驱动,可以支持到opengl4.3标准,一般双显的笔记本,程序默认启用的是集显,我机器的集显驱动有点老 ...

随机推荐

  1. Egret入门了解

    0.前言 这个星期没有什么事做,就想找点技术了解一下.前段时间看过Egret,用来开发HTML5小游戏.一开始以为很麻烦的,但是经过这两天了解了一下,如果用这个游戏引擎来开发一些简单的游戏,还是蛮方便 ...

  2. ssh以密钥的方式登录服务器时,只要有密钥可以登服务器,如果有密钥和公钥同时存在(在公钥没问题的情况下可以),但如果公钥有问题,就不能登录成功

    在~/.ssh/下如果只有密钥或公私同时存在时,都可以成功登录服务器,但!!!!!!如果公钥有换成别的服务器的公钥时,是无法登录远程的服务器!!!!

  3. An introduction to High Availability Architecture

    https://www.getfilecloud.com/blog/an-introduction-to-high-availability-architecture/ An introduction ...

  4. Java中使用Oracle的客户端 load data和sqlldr命令执行数据导入到数据库中

    Windows环境下测试代码: import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundExcep ...

  5. [Windows Azure] How to Monitor Cloud Services

    How to Monitor Cloud Services To use this feature and other new Windows Azure capabilities, sign up ...

  6. App store 应用审核由于 IPv6 网络问题被拒的一点分析

    App store 应用审核由于 IPv6 网络问题被拒的一点分析   六月以后陆续有一些软件提交市场的时候被拒了,症状基本就是无法登陆啥的.我们公司的应用也未能幸免. 很多同学也想了不少办法,申诉. ...

  7. 【编码】Base64编码

    简述 为什么叫Base64?个人理解是,基础的64个字符. 而它的作用?用基础的(可理解为可安全传输的)64个字符,来表示难以表示的二进制或对程序造成干扰的字符. Base64的编码过程 自行编码分析 ...

  8. Office_Visio_Pro_2007

    Office_Visio_Pro_2007http://pan.baidu.com/share/link?shareid=473782&uk=3474501992 解压后在文件夹里找到密钥[一 ...

  9. iis下php 500错误

      很不想用iis,然而客户不想增加机器,只好按客户的意思了.可是没想到发送短信以在本地 机器上是好的,在iis下直接500.   一开始以为是防火墙问题,后来检查了一下没有,再后来换了一个短信接口, ...

  10. WIN7或者WIN8上边框的异常问题的解决攻略

    //主要两个步骤://第一个步骤就是在CMainFrame::OnCreate里面增加 HINSTANCE hInst = LoadLibrary(_T("UxTheme.dll" ...