OpenGL学习笔记5:着色器
来源:互联网 发布:cf免费卡枪带软件 编辑:程序博客网 时间:2024/06/05 10:46
初识着色器语言
变量和数据类型
可用的数据类型只有4种:有符号整数,无符号整数,浮点数,布尔值。
OpenGL着色语言中没有指针和字符串或字符。返回值可以为void。
向量类型
所有4种基本数据类型都可以存储在二维、三维或者四维向量中:
OpenGL着色语言向量数据类型
vec4 v1;vec4 v2=vec4(1.0f,1.0f,1.0f,1.0f);vec4 v3=vec4(1.0f,1.0f,1.0f,1.0f);v1=v2+v3;v1+= vec4(1.0f,1.0f,1.0f,1.0f);v2*=4.0f;
我们用点号来确定多大4个向量元素的地址,但是可以使用下列3组标识符中的任意一组:xyzw、rgba和stpq。但不能混合到一个向量中使用。
v3.x=3.0f;v3.rg=vec2(3.0f,5.0f);v3.stpq= vec4(1.0f,1.0f,1.0f,1.0f);
向量数据类型还支持swizzle(调换)操作。例如,将颜色数据从RGB顺序转换到BGR顺序:
vColor.bgra=vColor.rgba;
矩阵类型
矩阵类型只支持浮点数。
OpenGL着色语言矩阵数据类型
实际上在OpenGL着色语言中,矩阵就是一个由向量组成的数组。可以对单独一列应用向量操作:
vec3 vec=mModelView[3].xyz;
矩阵也可以乘以向量,通常用在通过模型视图投影矩阵来对一个向量进行变换时。
矩阵构造函数:
mat4 vTransform=mat4(1.0f,1.0f,1.0f,1.0f, 1.0f,1.0f,1.0f,1.0f, 1.0f,1.0f,1.0f,1.0f, 1.0f,1.0f,1.0f,1.0f);
也可以如下方式构造单位矩阵:
mat4 v=mat4(1.0f);
存储限定符
限定符用于将变量标记为输入变量、输出变量或常量。
变量存储限定符
inout只能在一个函数中声明一个参数时使用,这是将一个值传递到一个函数并且允许这个函数修改并返回同一个变量值的唯一方法。
除非正在对一个多重采样缓冲区进行渲染,否则centroid限定符不会起任何作用。
默认情况下,参数将在两个着色器截断之间以一种透视正确的方法进行插补。我们可以通过noperspective关键词来指定一个非透视插值,或者甚至可以通过flat关键词二不进行插值。我们还可以选择使用smooth关键词来声明,这个变量时以一种透视正确的方法进行插补的,实际上是默认设置:
smooth out vec3 vSmoothValue;flat out vec3 vFlatColor;noperspective float vLinearlySmoothed;
真正的着色器
Shadedldentity着色器顶点程序
// The ShadedIdentity Shader// Vertex Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130in vec4 vColor;in vec4 vVertex;out vec4 vVaryingColor;void main(void) { vVaryingColor = vColor; gl_Position = vVertex;}
Shadedldentity着色器片段程序
// The ShadedIdentity Shader// Fragment Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130out vec4 vFragColor;in vec4 vVaryingColor;void main(void) { vFragColor = vVaryingColor; }
OpenGL着色语言版本
每个着色器的第一行非命令行都是指定这个着色器要求的OpenGL着色语言的最低版本版本,如果OpenGL驱动不支持,那么着色器将不会编译:
#version 330
OpenGL3.3版本
属性声明
属性是由OpenGL代码逐个顶点进行指定的,在顶点着色器中,这些属性指示简单地声明为
in;in vec4 vVertex;in vec4 vColor;
在GLSL中每个顶点程序都罪过可以有16个属性。
每个属性总是一个4分量向量,即使不适用所有的4汾江时也是如此。例如,我们指示指定了一个float值作为参数,那么在内部仍将占据4个浮点值的空间。
标记为in的变量时只读的。
声明输出
顶点程序的输出变量将成为要传送到片段着色器的顶点的颜色值,这个变量必须为片段朱搜而起声明一个in变量,否则在我们试图将着色器编译和连接到一起时,就会得到一个连接错误。
顶点动作
顶点程序的主体在批次中将为每个顶点执行一次:
void main(void){vVaryingColor=vColor;gl_Position=vVertex;}
变量gl_Position是一个预定义的内建4分量向量,它包含顶点着色器要求的一个输出。
片段三角形
在渲染一个图元(例如一个三角形)时,一旦3个顶点由顶点程序进行了处理,那么它们将组装成一个三角形,而这个三角形将由硬件进行光栅化。图形硬件确定独立片段属于屏幕上(或者更精确地,在颜色缓冲区中)的什么位置,并且为三角形中的每个片段(如果不进行任何多重采样的话则只是一个点)指定片段程序的一个实例。
片段程序的最终输出颜色是一个4分量浮点向量,我们如下声明这个向量:
out vec4 vFragColor;
输入片段着色器时经过平滑插值的颜色值,由顶点程序上游传入:
in vec4 vVaryingColor;
片段着色器只是将平滑插值的颜色分配给片段颜色:
void main(void){vFragColor=vVaryingColor;}
编译、绑定和连接
指定属性
Gluint gltLoadShaderPairWithAttributes(const char *szVertexProg,const char *szFragmentProg,……);
我们可能像下面这样调用该函数:
hShader=gltLoadShaderPairWithAttributes(“vertexProg.vp”,”fragmentProg.fp”,2,0,”vVertexPos”,1,”vNormal”);
对于为两个属性位置进行0和1的选择视任意性的,只要这个值在0~15范围之内。我们也可以选择7和13。不过,GLTools类GLBatch和GLTriangleBatch则使用一系列一致的属性位置,由typedef指定:
typedef enum GLT_SHADER_ATTRIBUTE{GLT_ATTRIBUTE_VERTEX=0,GLT_ATTRIBUTE_COLOR,GLT_ATTRIBUTE_NORMAL,GLT_ATTRIBUTE_TEXTURE0,GLT_ATTRIBUTE_TEXTURE1,GLT_ATTRIBUTE_TEXTURE2,GLT_ATTRIBUTE_TEXTURE3,GLT_ATTRIBUTE_LAST};
如果使用了这些属性位置标识符,我们就可以开始和GLShaderManager类中支持的存储着色器一起使用自己的着色器了。
设置源代码
我们的首要任务是创建两个着色器对象,分别对应顶点着色器和片段着色器:
hVertexShader=glCreateShader(GL_VERTEX_SHADER);hFragmentShader=glCreateShader(GL_FRAGMENT_SHADER);
然后使用gltLoadShaderFile加载着色器文本。
接着对顶点着色器和片段着色器应用下面几项操作:
GLchar *fsStringPtr[1];fsStringPtr[0]=(GLchar*)szShaderSrc;glShaderSource(shader,1,(const GLchar **)fsStringPtr,NULL);
编译着色器
编译着色器时一项简单的一次性工作:
glcompileShader(hVertexShader);glCompileShader(hFragmentShader);
为了检查失败,我们使用以GL_COMPILE_STATUS标记为参数的glGetShader函数:
glGetShaderiv(hVertexShader,GL_COMPILE_STATUS,&testVal);
如果返回的testVal值为GL_FALSE,那就说明源代码编译失败了。也可以打印出编译信息来查看出问题的原因。
进行连接和绑定
首先,我们要创建最终的着色器程序对象,并将顶点着色器和片段着色器与它绑定到一起:
hReturn=glCreateProgram();glAttachShader(hReturn,hVertexShader);glAttachShader(hReturn,hFragmentShader);
接着,将属性变量名绑定到指定的数字属性位置:
void glBindAttriblocation(Gluint shaderProg,GLuint attribLocation,const GLchar *szAttributeName);
它接受我们正在讨论的着色器的标识符,将要进行绑定的属性位置和属性变量的名称。我们常采用一个约定:位置属性变量名用vVertex,属性位置用GLT_ATTRIBUTE_VERTEX值(值0)
连接着色器
glLinkProgram(hReturn);
连接之后可以丢弃顶点着色器对象和片段着色器对象:
glDeleteShader(hVertexShader);glDeleteShader(hFragmentShader);
最后,当不再使用这个着色器程序时,需要删除它:
void glDeleteProgram(GLuint program);
使用着色器
要使用GLSL着色器,必须使用glUseProgram函数选定它:
glUseProgram(myShaderProgram);
接着调用Batch的Draw方法,即可应用对应的着色器效果:
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f };GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f };triangleBatch.Begin(GL_TRIANGLES, 3);triangleBatch.CopyVertexData3f(vVerts);triangleBatch.CopyColorData4f(vColors);triangleBatch.End();myIdentityShader = gltLoadShaderPairWithAttributes("ShadedIdentity.vp", "ShadedIdentity.fp", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");glUseProgram(myIdentityShader);triangleBatch.Draw();
Provoking Vertex
单个三角形,我们可以使用插值来实现插值渐变等特效,但是对于批次图形,这么做性能很低。使用flat存储标识符后,图元将只使用一个顶点的颜色,但是整个批次图形会进行插值渐变实现特效:
flat out vec4 vFlatColor;
对应的片段着色器的in变量也必须声明为flat。
void glProvokingVertex(GLenum provokeMode);provokeMode的合法值为GL_FIRST_VERTEX_CONVENTION和GL_LAST_VERTEX_CONVENTIONS(默认值)
着色器统一值
属性是每个顶点位置、表面法线和纹理坐标等都需要的,而统一值则用于为整个图元批次向保持不变的(统一(uniform)的)着色器传递数据。
创建一个统一值只需在变量声明开始时放置一个uniform关键词:
uniform float fTime;
统一值不能标记为in或out,它们也不能在着色器阶段之间进行插值,且它们总是只读的。
寻找统一值
在着色器进行编译和连接之后可以使用glGetUniformLocation函数来寻找统一值:
Glint glGetUniformLocation(GLuint shaderID,const GLchar* varName);
该函数返回在shaderID指定的着色器中由varName命名的变量的位置。-1则表示无法定位。
设置标量和向量统一值
一个单独的标量和向量数据类型可以在glUniform函数中使用下面的变量进行设置:
void glUniform1f(Glint location,GLfloat v0);void glUniform2f(Glint location,GLfloat v0,GLfloat v1);void glUniform3f(Glint location,GLfloat v0,GLfloat v1,GLfloat v2);void glUniform4f(Glint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3);void glUniform1i(Glint location,GLfloat v0);void glUniform2i(Glint location,GLfloat v0,GLfloat v1);void glUniform3i(Glint location,GLfloat v0,GLfloat v1,GLfloat v2);void glUniform4i(Glint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3);
例如:
uniform vec4 vColorValue;uniform bool bSomeFlag;Glint locColor,locFlag;locColor=glGetUniformLocation(myShader,” vColorValue”);locFlag=glGetUniformLocation(myShader,” bSomeFlag”);glUseProgram(myShader);glUniform4f(locColor,1.0f,0.0f,0.0f,1.0f);glUniform1i(locFlag,GL_FALSE);
布尔值也可以作为浮点值进行传递,0.0代表假,1.0代表真。
设置统一数组
glUniform函数还接受一个指针,假定指向一个数值数组:
void glUniform1fv(Glint location,GLuint count,GLfloat* v);void glUniform2fv(Glint location,GLuint count,GLfloat* v);void glUniform3fv(Glint location,GLuint count,GLfloat* v);void glUniform4fv(Glint location,GLuint count,GLfloat* v);void glUniform1fv(Glint location,GLuint count,GLint* v);void glUniform2fv(Glint location,GLuint count, GLint * v);void glUniform3fv(Glint location,GLuint count, GLint * v);void glUniform4fv(Glint location,GLuint count, GLint * v);
其中,count代表每个含有x分量的数组中的元素个数。
GLfloat vColors[2][4]={{1.0f,1.0f,1.0f,1.0f},{1.0f,0.0f,0.0f,1.0f}};glUniform4fv(iColorLocation,2,vColors);GLfloat fCalue=45.2f;glUniform1fv(iLocation,1,&fValue);
设置统一矩阵
下面的函数分别用来加载一个2X2,3X3,4X4矩阵:
glUniformMatrix2fv(Glint location,GLuint count,GLboolean transpose,const GLfloat *m);glUniformMatrix3fv(Glint location,GLuint count,GLboolean transpose,const GLfloat *m);glUniformMatrix4fv(Glint location,GLuint count,GLboolean transpose,const GLfloat *m);
变脸count代表指针参数m中存储的矩阵数量(我们可以使用矩阵数组)。transpose设为GL_TRUE表示这个值已经按照优先排序(OpenGL推荐的方式)进行存储,否则会导致这个矩阵在复制到着色器中时发生变换。
内建函数
三角函数
下标用anyFloat来表示float、vec2、vec3和vec4中的一种:
三角函数
指数函数
指数函数
几何函数
几何函数
矩阵函数
许多矩阵操作都是使用常规数学运算符进行的,不过还有一些有用的矩阵函数:
矩阵函数
向量相关函数
向量之间的比较,要使用下标列出的这些函数:
向量相关函数
常用函数
所有这些函数都能用于标量和向量数据类型的运算,并且也返回标量和向量数据类型:
矩阵函数 通用函数
模拟光线
简单漫射光
要确定一个指定顶点上光线的强度,我们需要两个向量:光源的方向和表面法线。
顶点照明
我们可以用点乘积的值与顶点的一个颜色值相乘,得到一个基于顶点光线强度的光照颜色值。:
float intensity=dot(vSurfaceNormal,vLightDirection);
点光源漫反射着色器
// Simple Diffuse lighting Shader// Vertex Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130// 输入每个顶点... 位置和法向in vec4 vVertex;in vec3 vNormal;// 设置每个批次uniform vec4 diffuseColor; uniform vec3 vLightPosition;uniform mat4 mvpMatrix;uniform mat4 mvMatrix;uniform mat3 normalMatrix;// 片段程序颜色smooth out vec4 vVaryingColor;void main(void) { // 获取表面法线的视觉坐标 vec3 vEyeNormal = normalMatrix * vNormal; // 获取顶点位置的视觉坐标 vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // 获取到光源的向量 vec3 vLightDir = normalize(vLightPosition - vPosition3); // 从点乘积得到漫反射强度 float diff = max(0.0, dot(vEyeNormal, vLightDir)); // 用强度乘以漫反射颜色,将alpha值设为1.0 vVaryingColor.rgb = diff * diffuseColor.rgb; vVaryingColor.a = diffuseColor.a; // 不要忘记对多边形进行变换 gl_Position = mvpMatrix * vVertex;}
ADS光照模型
ADS代表环境光(Ambient)、漫射光(Diffuse)和镜面光(Specular)。
环境光
为了计算一个环境光源对最终顶点颜色的影响,环境光材质的性质由环境光的值来度量(就是将这两个颜色值相乘)。
uniform vec3 vAmbientMaterial;uniform vec3 vAmbientLight;vec3 vAmbientColor=vAmbientMaterial*vAmbientLight;
漫射光
在ADS光照模式下,漫反射材质和光照值相乘,所得结果由表面法线和光照向量的点乘积(漫反射强度)进行缩放。
uniform vec3 vDiffuseMaterial;uniform vec3 vDiffuseLight;float fDotProduct=max(0.0,dot(vNormal,vLightDir));vec3 vDiffuseColor=vDiffuseMaterial*vDiffuseLight*fDotProduct;
点乘积可能是一个负数,但会被0代替。
镜面光
首先我们必须找到被表面法线反射的向量和反向的光线向量。随后这两个向量的点乘积将取“反光度”(shininess)次幂。反光度数值越大,结果得到镜面反射的高亮度区越小。
uniform vec3 vSpecularMaterial;uniform vec3 vSpecularLight;float shininess=128.0;vec3 vReflection=reflect(-vLightDir,vEyeNormal);float EyeReflectionAngle=max(0.0,dot(vEyeNormal,vReflection));fSpec=pow(EyeReflectionAngle,shininess);vec3 vSpecularColor=vSpecularLight*vSpecularMaterial*fSpec;
反光度参数也可以是统一值。传统上最高镜面指数被设置为128,大于它效果将逐渐减弱。
ADS着色器
基于前三个例子,顶点最终的颜色可以以如下方式进行计算:
vVertexColor=vAmbientColor+vDiffuseColor+vSpecularColor;
示例:
// ADS Point lighting Shader// Vertex Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130// 输入每个顶点... 位置和法向in vec4 vVertex;in vec3 vNormal;// 设置每个批次uniform vec4 ambientColor;uniform vec4 diffuseColor; uniform vec4 specularColor;uniform vec3 vLightPosition;uniform mat4 mvpMatrix;uniform mat4 mvMatrix;uniform mat3 normalMatrix;// 片段程序颜色smooth out vec4 vVaryingColor;void main(void) { // 获取表面法线的视觉坐标 vec3 vEyeNormal = normalMatrix * vNormal; // 获取顶点位置的视觉坐标 vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // 获取到光源的向量 vec3 vLightDir = normalize(vLightPosition - vPosition3); // 懂点乘积得到漫反射强度 float diff = max(0.0, dot(vEyeNormal, vLightDir)); // 用强度乘以漫反射颜色,将alpha值设为1.0 vVaryingColor = diff * diffuseColor; // 添加环境光 vVaryingColor += ambientColor; // 镜面光 vec3 vReflection = normalize(reflect(-vLightDir, vEyeNormal)); float spec = max(0.0, dot(vEyeNormal, vReflection)); if(diff != 0) { float fSpec = pow(spec, 128.0); vVaryingColor.rgb += vec3(fSpec, fSpec, fSpec); } // 不要忘记对多边形进行变换 gl_Position = mvpMatrix * vVertex;}
Phong着色
在Phong着色时,我们并不在顶点之间进行颜色值插值,而是在顶点之间进行表面法线插值。
顶点着色器
// ADS Point lighting Shader// Vertex Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130// 输入每个顶点... 位置和法向in vec4 vVertex;in vec3 vNormal;uniform mat4 mvpMatrix;uniform mat4 mvMatrix;uniform mat3 normalMatrix;uniform vec3 vLightPosition;// 片段程序颜色smooth out vec3 vVaryingNormal;smooth out vec3 vVaryingLightDir;void main(void) { // 获取表面法线的视觉坐标 vVaryingNormal = normalMatrix * vNormal; // 获取顶点位置的视觉坐标 vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // 获取到光源的向量 vVaryingLightDir = normalize(vLightPosition - vPosition3); // 不要忘记对多边形进行变换 gl_Position = mvpMatrix * vVertex;}
片段着色器
// ADS Point lighting Shader// Fragment Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130out vec4 vFragColor;uniform vec4 ambientColor;uniform vec4 diffuseColor; uniform vec4 specularColor;smooth in vec3 vVaryingNormal;smooth in vec3 vVaryingLightDir;void main(void) { // 从点乘积得到漫反射强度 float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir))); // 用强度乘以漫反射颜色,将alpha值设为1.0 vFragColor = diff * diffuseColor; // 天假环境光 vFragColor += ambientColor; // 镜面光 vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal))); float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection)); if(diff != 0) { float fSpec = pow(spec, 128.0); vFragColor.rgb += vec3(fSpec, fSpec, fSpec); }}
访问纹理
照亮纹理单元
我们在ADSPhong着色器中添加一个纹理:
// ADS Point lighting Shader// Fragment Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130out vec4 vFragColor;uniform vec4 ambientColor;uniform vec4 diffuseColor; uniform vec4 specularColor;uniform sampler2D colorMap;smooth in vec3 vVaryingNormal;smooth in vec3 vVaryingLightDir;smooth in vec2 vTexCoords;void main(void) { // 从点乘积得到漫反射强度 float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir))); // 用强度乘以漫反射颜色,将alpha值设为1.0 vFragColor = diff * diffuseColor; // 添加环境光 vFragColor += ambientColor; //纹理采样 vFragColor *= texture(colorMap, vTexCoords); // 镜面光 vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal))); float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection)); if(diff != 0) { float fSpec = pow(spec, 128.0); vFragColor.rgb += vec3(fSpec, fSpec, fSpec); } }
示例中出现了一个新的变量类型sampler2D:
uniform sampler2D colorMap
一个采样器实际上就是一个整数(我们使用glUniform1)来设置它的值,它代表我们将要采样的纹理所绑定的纹理单元。2D表示这是一个2D纹理,还可以是1D和3D。目前来说这个值总是设为0。
texture函数使用插值纹理坐标对纹理进行采样,并将颜色值直接分配给片段颜色:
vFragColor *= texture(colorMap, vTexCoords);
丢弃片段
颜色的混合计算过程并不简单,而如果某个片段的alpha为0,或非常接近0,则该片段实际上是不可见的,也就是不需要计算。
要测试一个alpha值是不是小于0.1,我们可以如下操作:
if(vColorValue.a<0.1f) discard;
例如,对一个图像按红色值进行采样丢弃(比如燃烧效果):
首先,我们需要为采样器和倒计时器提供统一值:
uniform sampler2D cloudTexture;uniform float dissolveFactor;
然后对纹理进行取样,并确定红色值是否低于倒计时值,最终完全丢弃这个片段:
vec4 vCloudSample=texture(cloudTexture,vVaryingTexcoord);if(vCloudSample.r<dissolveFactor) discard;
卡通着色(Cell Shading)——将纹理单元作为光线
卡通着色将一个一维纹理贴图作为查询表,使用纹理贴图中的纯色(使用GL_NEAREST)填充集合图形。
基本思路是,使用漫射光照强度作为纹理坐标添加到一个包含逐渐变亮颜色表的一维纹理中。
// Cell lighting Shader// Vertex Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130// 输入每个顶点... 位置和法向in vec4 vVertex;in vec3 vNormal;smooth out float textureCoordinate;uniform vec3 vLightPosition;uniform mat4 mvpMatrix;uniform mat4 mvMatrix;uniform mat3 normalMatrix;void main(void) { // 获取表面法线的视觉坐标 vec3 vEyeNormal = normalMatrix * vNormal; // 获取顶点位置的视觉坐标 vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // 获取到光源的向量 vec3 vLightDir = normalize(vLightPosition - vPosition3); // 从点乘积得到漫反射强度 textureCoordinate = max(0.0, dot(vEyeNormal, vLightDir)); // 不要忘记对多边形进行变换 gl_Position = mvpMatrix * vVertex;}
除了经过变换的几何图形位置外,这个着色器唯一的输出就是一个插值纹理坐标textureCoordinate,它表示一个单独的float。
卡通着色器的片段程序只是对一维纹理进行采样,并将它的值写入帧缓冲区片段:
// Cell lighting Shader// Fragment Shader// Richard S. Wright Jr.// OpenGL SuperBible#version 130uniform sampler1D colorTable;out vec4 vFragColor;smooth in float textureCoordinate;void main(void) { vFragColor = texture(colorTable, textureCoordinate); }
转载请注明出处:http://blog.csdn.net/ylbs110/article/details/51868381
示例
着色器
顶点着色器
#version 130in vec4 vVertex;in vec3 vNormal;smooth out float textureCoordinate;uniform vec3 vLightPosition;uniform mat4 mvpMatrix;uniform mat4 mvMatrix;uniform mat3 normalMatrix;smooth out vec3 vVaryingNormal;smooth out vec3 vVaryingLightDir;void main(void) { // 获取表面法线的视觉坐标 vVaryingNormal = normalMatrix * vNormal; // 获取顶点位置的视觉坐标 vec4 vPosition4 = mvMatrix * vVertex; vec3 vPosition3 = vPosition4.xyz / vPosition4.w; // 获取到光源的向量 vVaryingLightDir = normalize(vLightPosition - vPosition3); // 从点乘积得到漫反射强度 textureCoordinate = max(0.0, dot(vVaryingNormal, vVaryingLightDir)); // 不要忘记对多边形进行变换 gl_Position = mvpMatrix * vVertex;}
片段着色器
#version 130uniform sampler1D colorTable;vec4 sampler1DColor;out vec4 vFragColor;uniform vec4 ambientColor;uniform vec4 diffuseColor; uniform vec4 specularColor;uniform float dissolveFactor;smooth in vec3 vVaryingNormal;smooth in vec3 vVaryingLightDir;smooth in float textureCoordinate;void main(void) { //对1维纹理采样 sampler1DColor = texture(colorTable, textureCoordinate); //当1维纹理的红色值低于倒计时值时丢弃对应片段 if(sampler1DColor.r < dissolveFactor) discard; // 从点乘积得到漫反射强度 float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir))); // 用强度乘以漫反射颜色,将alpha值设为1.0 vFragColor = diff * diffuseColor; // 添加环境光 vFragColor += ambientColor; // 镜面光 vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal))); float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection)); if(diff != 0) { float fSpec = pow(spec, 128.0); vFragColor.rgb =sampler1DColor.rgb+ vec3(fSpec, fSpec, fSpec); } }
调用代码
#include "stdafx.h"#include <GLTools.h>#include <GLShaderManager.h>#include <GLFrustum.h>#include <GLBatch.h>#include <GLFrame.h>#include <GLMatrixStack.h>#include <GLGeometryTransform.h>#include <StopWatch.h>#include <math.h>#include <stdio.h>#include <math.h>#define GLUT_DISABLE_ATEXIT_HACK#include <GLUT.H>/** 当libjpeg-turbo为vs2010编译时,vs2015下静态链接libjpeg-turbo会链接出错:找不到__iob_func,* 增加__iob_func到__acrt_iob_func的转换函数解决此问题,* 当libjpeg-turbo用vs2015编译时,不需要此补丁文件*/#if _MSC_VER>=1900#include "stdio.h" _ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);#ifdef __cplusplus extern "C"#endif FILE* __cdecl __iob_func(unsigned i) { return __acrt_iob_func(i);}#endif /* _MSC_VER>=1900 */GLFrame viewFrame;GLFrustum viewFrustum;GLTriangleBatch torusBatch;GLMatrixStack modelViewMatrix;GLMatrixStack projectionMatrix;GLGeometryTransform transformPipeline;GLShaderManager shaderManager;GLuint myFristShader; // The dissolving light shaderGLint locAmbient; // The location of the ambient colorGLint locDiffuse; // The location of the diffuse colorGLint locSpecular; // The location of the specular colorGLint locLight; // The location of the Light in eye coordinatesGLint locMVP; // The location of the ModelViewProjection matrix uniformGLint locMV; // The location of the ModelView matrix uniformGLint locNM; // The location of the Normal matrix uniformGLint locColorTable; // The location of the color tableGLint locDissolveFactor; // The location of the dissolve factorGLuint texture;// This function does any needed initialization on the rendering// context. void SetupRC(void){ glClearColor(0.025f, 0.25f, 0.25f, 1.0f); glEnable(GL_DEPTH_TEST); shaderManager.InitializeStockShaders(); viewFrame.MoveForward(4.0f); gltMakeTorus(torusBatch, .80f, 0.25f, 52, 26); myFristShader = gltLoadShaderPairWithAttributes("myFristShader.vp", "myFristShader.fp", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_NORMAL, "vNormal"); //获取着色器统一值 locAmbient = glGetUniformLocation(myFristShader, "ambientColor"); locDiffuse = glGetUniformLocation(myFristShader, "diffuseColor"); locSpecular = glGetUniformLocation(myFristShader, "specularColor"); locLight = glGetUniformLocation(myFristShader, "vLightPosition"); locMVP = glGetUniformLocation(myFristShader, "mvpMatrix"); locMV = glGetUniformLocation(myFristShader, "mvMatrix"); locNM = glGetUniformLocation(myFristShader, "normalMatrix"); locColorTable = glGetUniformLocation(myFristShader, "colorTable"); locDissolveFactor = glGetUniformLocation(myFristShader, "dissolveFactor"); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_1D, texture); GLubyte textureData[4][3] = { 32, 0, 0, 64, 0, 0, 128, 0, 0, 255, 0, 0 }; glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);}// Cleanupvoid ShutdownRC(void){ glDeleteTextures(1, &texture);}// Called to draw scenevoid RenderScene(void){ static CStopWatch rotTimer; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); modelViewMatrix.PushMatrix(viewFrame); modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 10.0f, 0.0f, 1.0f, 0.0f); GLfloat vEyeLight[] = { -100.0f, 100.0f, 100.0f }; GLfloat vAmbientColor[] = { 0.1f, 0.1f, 0.1f, 1.0f }; GLfloat vDiffuseColor[] = { 0.1f, 1.0f, 0.1f, 1.0f }; GLfloat vSpecularColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //选定要使用的着色器 glUseProgram(myFristShader); //设置统一数组 glUniform4fv(locAmbient, 1, vAmbientColor); glUniform4fv(locDiffuse, 1, vDiffuseColor); glUniform4fv(locSpecular, 1, vSpecularColor); glUniform3fv(locLight, 1, vEyeLight); //设置统一矩阵值 glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix()); glUniformMatrix4fv(locMV, 1, GL_FALSE, transformPipeline.GetModelViewMatrix()); glUniformMatrix3fv(locNM, 1, GL_FALSE, transformPipeline.GetNormalMatrix()); glUniform1i(locColorTable, 0); //使用倒计时来设置丢弃片段的统一值 float fFactor = fmod(rotTimer.GetElapsedSeconds(), 10.0f); fFactor /= 10.0f; glUniform1f(locDissolveFactor, fFactor); torusBatch.Draw(); modelViewMatrix.PopMatrix(); glutSwapBuffers(); glutPostRedisplay();}void ChangeSize(int w, int h){ // Prevent a divide by zero if (h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h); viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 100.0f); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);}///////////////////////////////////////////////////////////////////////////////// Main entry point for GLUT based programsint main(int argc, char* argv[]){ gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("Cell (toon) shading"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } SetupRC(); glutMainLoop(); ShutdownRC(); return 0;}
运行结果
可以看到一个实现了冯氏着色的使用一维为例进行卡通渲染的花托通过红色值的大小实现了消融效果。
- OpenGL学习笔记5:着色器
- OpenGL学习笔记5:着色器2
- opengl es学习笔记2(着色器补充,示例)
- 用glew,glfw实现opengl-学习笔记3着色器
- OpenGL学习笔记 (3) —— 着色
- opengl学习笔记之指定着色模型
- 几何着色器 Opengl Geometry Shaders 笔记
- openGL着色器学习(一)
- opengl 学习资料,详细介绍着色器
- OpenGL学习:几何着色器(geometry shader)
- 小朱opengl学习笔记(三)------着色器的详细学习
- OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译
- 学习笔记总结:Android中OpenGL ES的着色语言
- OpenGL 着色器语言
- OpenGL着色器语言
- opengl顶点着色器
- OpenGL ES 着色器
- OpenGL着色器介绍
- 第2章 一切都是对象
- Log4j的学习--Log4j 配置文件
- SGU 495 Kids and Prizes
- 【一天一道LeetCode】#160. Intersection of Two Linked Lists
- 使用SpEL表达式装配Bean
- OpenGL学习笔记5:着色器
- POJ 2182 Lost Cows
- 向量夹角顺时针或逆时针,交叉口向左拐向右拐的问题
- 第3章 操作符
- Kinect基础之在WinForm(C#)中显示彩色数据和深度数据
- Leetcode-minimum-depth-of-binary-tree
- STL基础(二)
- 第4章 控制执行流程
- MongoDB主从复制