Shader学习笔记,通过GLSL实现(一)

最近一直在专心研究利用GLSL编写Shader,写点东西将自己学的总结一下,把自己学习shader的经历分享一下,希望能对有兴趣学习shader的同学有些帮助,但这些玩意还算不上教程,很多都是我自己在学习中的问题以及如何解决的,有什么不足还请各位指出,想要系统的学习GLSL的话还是推荐大家看《OpenGL Shading Language 3rd Edition》,但如果你对OpenGL也一无所知的话,那就请从《OpenGL Programming Guide 7th Edition》读起了。

 

本次主要是给大家一个GLSL的简单示例,对于一门新的语言或者技术,初学者入门总是要通过阅读一些示例程序,我在初学GLSL的时候也是这样,但是上网找了一圈发现,网上的大牛们所给的示例大部分都是仅仅有shader的代码,而没有相应的OpenGL应用程序,好不容易找到一个完整的也只是简简单单的渲染一个2D三角形,但是如果如果是想渲染一个带有光照的3D模型呢?如何在OpenGL程序中加入对shader的支持呢?如何向shader中传递数据呢?等等这些问题虽然书中会有答案,但是却缺少一个完整的示例将他们系统的整合起来,下面我就给出一个利用GLSL+OpenGL实现最基本的Binn-phong+lamber光照的的程序,至于具体算法就不细讲了,有兴趣的可以自己研究一下,很简单的。

 

在本程序中,顶点着色器完成了几乎所有任务,包括顶点由物体坐标系到投影坐标系的变换以及所有的光照计算,而片段着色器只是完成了颜色数据的传递,但在后面由于一些算法的要求(例如bumpmap),我们还会将光照计算移到片段着色器中,这里要注意,其实除了个别一些功能以外(例如顶点的空间变化或者纹理查询),很多的计算均可以放在顶点着色器或者片段着色器里,其区别只是计算速度与图像的质量。下面是着色器代码:

顶点着色器(Vertex Shader):

uniform vec3 lightposition;//光源位置 
uniform vec3 eyeposition;//相机位置 
uniform vec4 ambient;//环境光颜色 
uniform vec4 lightcolor;//光源颜色 
uniform float Ns;//高光系数 
uniform float attenuation;//光线的衰减系数 

varying vec4 color;//向片段着色其传递的参数 

void main() 
{ /*很多示例中都是利用uniform参数从应用程序中向shader里传递当前模型视图矩阵和模型视图投影矩阵,其实对于初学者来说,我们大可以先用GLSL的内建变量:gl_ModelViewMatrix和gl_ModelViewProjectionMatrix代替,而顶点坐标的变换则直接可以利用内建函数ftransform()实现。当然,如果你想自己传递这些参数也是可以的,后面会介绍一下。而gl_Vertex和gl_Normal则分别表示当前传入的顶点的物体坐标系坐标和表面法向量,gl_Position则是用来传输投影坐标系内顶点坐标的内建变量。注意内建变量是不用声明的,直接使用就行*/  vec3 ECPosition = vec3(gl_ModelViewMatrix * gl_Vertex); 
vec3 N = normalize(gl_NormalMatrix * gl_Normal); 
vec3 L = normalize(lightposition - ECPosition); 
vec3 V = normalize(eyeposition - ECPosition); 
vec3 H = normalize(V + L); 

vec3 diffuse = lightcolor * max(dot(N , L) , 0); 
vec3 specular = lightcolor * pow(max(dot(N , H) , 0) , Ns) * attenuation; 

color = vec4(clamp((diffuse + specular) , 0.0 , 1.0) , 1.0); 
color = color + ambient; 

gl_Position = ftransform(); 
} 片段着色器(Fragment Shader): //这里的的varying变量名称要与顶点着色器里的一样,否则会编译错误  varying vec4 color; 

void main() 
{ //gl_FragColor是输出片段颜色的内建变量,凡是以gl_开头的变量都是内建变量
gl_FragColor = color;
 
} 
其实shader程序不是很难写,我学习的过程中主要是卡在了OpenGL程序和shader程序的关联上,其实后来看看并不是太难,总结起来也就是读取shader代码;建立、编译shader;向shader中传递数据,其他的和一般的OpenGL程序差不多,下面给出OpenGL程序: //头文件是我自己封装的工具包,功能包括:读取shader源文件;初始化shader和program等  #include <GLSL_Toolkit.h> 

GLfloat ambient[4] = {1.0 , 0.0 , 0.0 , 1.0}; 
GLfloat lightcolor[4] = {1.0 , 1.0 , 1.0 , 1.0}; 
GLfloat eyeposition[3] = {0.0 , 10.0 , 30.0}; 
GLfloat skycolor[3] = {0.7 , 0.7 , 1.0}; 
GLfloat groundcolor[3] = {0.2 , 0.2 , 0.2}; 
GLfloat Ns = 8; 
GLfloat attenuation = 0.1; 
GLfloat objectSize = 15.0; 
GLuint program; 
GLuint vShader , fShader; 

 

void myInit() 
{ 
glewInit(); 
glClearColor(0.0 , 0.0 , 0.0 , 1.0); 
glEnable(GL_DEPTH_TEST); 

//读取源文件 
const GLchar *vShaderSource = readShaderSource("GLSL_sample2.vert"); 
const GLchar *fShaderSource = readShaderSource("GLSL_sample2.frag"); 
//初始化shader和program 
vShader = buildShader(&vShaderSource , GL_VERTEX_SHADER); 
fShader = buildShader(&fShaderSource , GL_FRAGMENT_SHADER); 
program = buildShaderProgram(vShader , fShader); 
} 

void myReshape(int w , int h) 
{ 
glViewport(0 , 0 , (GLsizei)w , (GLsizei)h); 
glMatrixMode(GL_PROJECTION); 
glLoadIdentity(); 
gluPerspective(90 , 1 , 0.1 , 1000.0); 
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity(); 
gluLookAt(eyeposition[0] , eyeposition[1] , eyeposition[2] , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , 0.0); 
} 

void myDisplay() 
{ 
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
glColor3f(1.0 , 1.0 , 1.0); 

/*这里是程序比较重要的地方:向给shader中的uniform变量赋值。注意赋值函数使用的位置,只有当一个program启用的时候,里面的uniform变量才会被分配索引,所以过早的对uniform变量赋值会长生错误。同时注意函数的格式*/ 
glUseProgram(program); 
glUniform3f(glGetUniformLocation(program , "lightposition") , 30.0 , 30.0 , 30.0); 
glUniform3f(glGetUniformLocation(program , "eyeposition") , eyeposition[0] , eyeposition[1] , eyeposition[2]); 
glUniform4f(glGetUniformLocation(program , "ambient") , ambient[0] , ambient[1] , ambient[2] , ambient[3]); 
glUniform4f(glGetUniformLocation(program , "lightcolor") , lightcolor[0] , lightcolor[1] , lightcolor[2] , lightcolor[3]); 
glUniform1f(glGetUniformLocation(program , "Ns") , Ns); 
glUniform1f(glGetUniformLocation(program , "attenuation") , attenuation); 
/*刚开始我以为要利用shader进行渲染就要必须将模型变为一个一个顶点信息向shader中传入,但其实并不是这样,我们同样可以利用glut的内建模型或者自己动手载入模型,和平时一样。同时注意一点,glut的一些内建模型只包含平面法线,没有纹理坐标,所以在后面我们给模型添加纹理的时候有些就无法显示出纹理*/ 
glutSolidTeapot(objectSize); 
glutSwapBuffers(); 
} 

void main(int argc , char** argv) 
{ 
glutInit(&argc , argv); 
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); 
glutInitWindowPosition(100 , 100); 
glutInitWindowSize(600 , 600); 
glutCreateWindow("GLSL_example2"); 
myInit(); 
glutReshapeFunc(myReshape); 
glutDisplayFunc(myDisplay); 
glutIdleFunc(myDisplay); 
glutMainLoop(); 
} 
程序效果:  
前面说到了我们自己向shader中传递矩阵数据,这里简单说一下:

在程序中向声明个变量,例如GLfloat modelViewMatrix[16];

然后在完成视图模型矩阵的设置后调用glGetFloatv(GL_MODELVIEW_MATRIX ,modelViewMatrix);

然后在program启用后调用glUniformMatrix4fv(glGetUniformLocation("modelViewMatrix"), 1 , GL_FALSE , modelViewMatrix);

 

这样就完成了向shader中传递我们自己的矩阵数据。 总的说来整个程序并不是很难,但我是折腾了有一阵才搞成功,估计还是我太水了的缘故吧。。。    

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值