因为对不同的场景以及不同的材质需要使用不同的着色器,因此需要使用多个着色器,而不是单个,单个着色器是无法绘制出很复杂的场景,因为有的物体没有纹理而有的有纹理以及别的效果
着色器语言就是 GLSL格式的代码,我们可以先把顶点和片元着色器分别写在.glsl后缀名的文件中,如果你的编译器支持这种语法检测,那么代码就会有颜色,而不仅仅是字符串样式显示,尽管着色器代码本身就是字符串
下面是将要绘制的单色立方体的着色器文件
single_vertex_shader.glsl
// 顶点着色器 attribute vec4 a_Position; attribute vec4 a_Normal; uniform mat4 u_MvpMatrix; uniform mat4 u_NormalMatrix; varying vec4 v_Color; void main(){ vec3 lightDirection = vec3(0.0, 0.0, 1.0); // 灯光的位置 在世界坐标系中 vec4 color = vec4(0.5, 0.5, 0.8, 1.0);// 表面颜色 gl_Position = u_MvpMatrix * a_Position; // 计算后的顶点坐标 vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));//标准化法向量 float nDot = max(dot(normal, lightDirection), 0.0); v_Color = vec4(color.rgb * nDot, color.a); }single_fragment_shader.glsl
precision mediump float; varying vec4 v_Color; void main(){ gl_FragColor = v_Color }从外部文件加载着色器,其实就是加载.glsl文件,读取里面的数据,然后通过顶点着色器和片元着色器创建程序对象
关于如何加载外部文件的方法,在上一篇文章中有介绍,你也可以直接去我的gitee上仓库的lib目录直接下载加载器
加载完.glsl文件之后,就可以使用这两个着色器程序了,创建program对象,下面是两个程序对象,通过四个着色器代码创建得到的
var singleProgram = createProgram(gl, single_vertex_shader, single_fragment_shader); var textureProgram = createProgram(gl, texture_vertex_shader,texture_fragment_shader);采用javascript对象属性的方式,把每个变量的存储地址挂载到程序对象上,便于使用
// 获取 singleProgram 中的 attribute 和 uniform 变量存储位置 singleProgram.a_Position = gl.getAttribLocation(singleProgram, 'a_Position'); singleProgram.a_Normal = gl.getAttribLocation(singleProgram, 'a_Normal'); singleProgram.u_MvpMatrix = gl.getUniformLocation(singleProgram, 'u_MvpMatrix'); singleProgram.u_NormalMatrix = gl.getUniformLocation(singleProgram, 'u_NormalMatrix'); // 获取 textureProgram 中的 attribute 和 uniform 变量存储位置 textureProgram.a_Position = gl.getAttribLocation(textureProgram, 'a_Position'); textureProgram.a_Normal = gl.getAttribLocation(textureProgram, 'a_Normal'); textureProgram.a_TexCoord = gl.getAttribLocation(textureProgram, 'a_TexCoord'); textureProgram.u_MvpMatrix = gl.getUniformLocation(textureProgram, 'u_MvpMatrix'); textureProgram.u_NormalMatrix = gl.getUniformLocation(textureProgram, 'u_NormalMatrix'); textureProgram.u_Sampler = gl.getUniformLocation(textureProgram, 'u_Sampler');因为创建的是两个立方体,所以它们的顶点数据和顶点索引以及法线数据都是可以共用的
var cube = initVertexBuffers(gl); if (!cube) { console.log('failed to set the vertex information'); return false; }上面通过initVertexBuffers()方法来设置顶点信息
设置完公用的顶点信息后,一个立方体是需要加载贴图的,因此需要把贴图的数据给加载到贴图立方体的片元着色器上,此处是通过initTexture(gl, program)方法来进行设置
var texture = initTextures(gl, textureProgram); if (!texture) { console.log('failed to intialize the texture.'); return false; }设置完以上数据之后,就可以开始绘制
因为是三维物体,有z轴深度,需要开启深度
gl.enable(gl.DEPTH_TEST) // 设置canvas清空后的颜色 gl.clearColor(0.0, 0.5, 0.5, 1.0)下面就可以在循环渲染方法中,进行帧刷新渲染
实际上在真正的项目开发过程中,重复这些步骤是很繁琐的,对一些反复的操作封装比较好的引擎例如three.js它隐藏了webgl的内部执行流程,同时也内置了很多的着色器,每一种材质以及灯光的添加都对应了一种着色器,这些着色器是可以进行修改的,并且three.js也支持自己去写着色器以及原生webgl代码
https://ithanmang.gitee.io/webgl-notes/home/07-高级技术示例/08-使用不同着色器.html
