51 WebGL切换着色器
来源:互联网 发布:淘宝代充可靠吗 编辑:程序博客网 时间:2024/06/07 04:50
到目前为止,以前的程序都是用了一个着色器(顶点着色器和片元着色器),如果一个着色器就能绘制出场景中所有的物体,那就没有问题。然而事实是,对不同物体经常需要使用不同的着色器来绘制,每个着色器中可能有非常复杂的逻辑以实现各种不同的效果。我们可以准备多个着色器,然后根据需要来切换使用它们。
实现切换着色器
为了切换着色器,需要先创建多个着色器程序对象,然后再进行绘制前选择使用的程序对象。以前在39 WebGL着色器和着色器程序对象:initShader()函数的作用中介绍了如何创建程序对象。我们使用gl.useProgram()函数来进行切换。由于现在需要显式地操作着色器和程序对象,所以不能再使用initShaders()函数。但是,可以使用定义在cuon-utils.js中的createProgram()函数,实际上initShaders()函数内部也是调用该函数来创建着色器对象的。
下面是示例程序的流程步骤,由于它创建了两个程序对象,做了两轮相同的操作,所以看上去有点长。关键的代码实际上很简单。
(1)准备用来绘制单色立方体的着色器。
(2)准备用来绘制纹理立方体的着色器。
(3)调用createProgram()函数,利用第一步创建出的着色器,创建着色器程序对象。
(4)调用createProgram()函数,利用第二步创建出的着色器,创建着色器程序对象。
(5)调用gl.useProgram()函数,指定使用第3步创建出的着色器程序对象。
(6)通过缓冲区对象向着色器中传入attribute变量并开启之。
(7)绘制单色立方体。
(8)调用gl.useProgram()函数,指定使用第4步创建出的着色器程序对象。
(9)通过缓冲区对象向着色器传入attribute变量并开启之。
(10)绘制纹理立方体。
示例代码:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Title</title> <style> body { margin: 0; overflow: hidden; } #canvas { margin: 0; display: block; } </style></head><body onload="main()"><canvas id="canvas" height="800" width="800"></canvas></body><script src="lib/webgl-utils.js"></script><script src="lib/webgl-debug.js"></script><script src="lib/cuon-utils.js"></script><script src="lib/cuon-matrix.js"></script><script> //设置WebGL全屏显示 var canvas = document.getElementById("canvas"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; //单色立方体的着色器 var solidVertexShaderSource = "" + "attribute vec4 a_Position;\n" + "attribute vec4 a_Normal;\n" + "uniform mat4 u_MvpMatrix;\n" + "uniform mat4 u_NormalMatrix;\n" + "varying vec4 v_Color;\n" + "void main(){\n" + " vec3 lightDirection = vec3(0.0,0.0, 1.0);\n" +//设置灯光的坐标位置(世界坐标下) " vec4 color = vec4(0.0, 1.0, 1.0, 1.0);\n" + //设置立方体的颜色 " gl_Position = u_MvpMatrix * a_Position;\n" + " vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n" + " float nDotL = max(dot(normal, lightDirection), 0.0);\n" + " v_Color = vec4(color.rgb * nDotL, color.a);\n" + "}\n"; var solidFragmentShaderSource = "" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec4 v_Color;\n" + "void main(){\n" + " gl_FragColor = v_Color;\n" + "}\n"; //纹理立方体的着色器 var textureVertexShaderSource = "" + "attribute vec4 a_Position;\n" + "attribute vec4 a_Normal;\n" + "attribute vec2 a_TexCoord;\n" + "uniform mat4 u_MvpMatrix;\n" + "uniform mat4 u_NormalMatrix;\n" + "varying float v_NdotL;\n" + "varying vec2 v_TexCoord;\n" + "void main(){\n" + " vec3 lightDirection = vec3(0.0, 0.0, 1.0);\n" + " gl_Position = u_MvpMatrix * a_Position;\n" + " vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n" + " v_NdotL = max(dot(normal, lightDirection), 0.0);\n" + " v_TexCoord = a_TexCoord;\n" + "}\n"; var textureFragmentShaderSource = "" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "uniform sampler2D u_Sampler;\n" + "varying vec2 v_TexCoord;\n" + "varying float v_NdotL;\n" + "void main(){\n" + " vec4 color = texture2D(u_Sampler, v_TexCoord);\n" + " gl_FragColor = vec4(color.rgb * v_NdotL, color.a);\n" + "}\n"; function main() { var canvas = document.getElementById("canvas"); var gl = getWebGLContext(canvas); if (!gl) { console.log("你的浏览器不支持WebGL"); return; } //初始化两个程序对象的着色器 var solidProgram = createProgram(gl,solidVertexShaderSource,solidFragmentShaderSource); var textureProgram = createProgram(gl,textureVertexShaderSource,textureFragmentShaderSource); if(!solidProgram || !textureProgram){ console.log("创建程序对象失败"); return; } //获取单色立方体的attribute和uniform存储位置 solidProgram.a_Position = gl.getAttribLocation(solidProgram, "a_Position"); solidProgram.a_Normal = gl.getAttribLocation(solidProgram, "a_Normal"); solidProgram.u_MvpMatrix = gl.getUniformLocation(solidProgram, "u_MvpMatrix"); solidProgram.u_NormalMatrix = gl.getUniformLocation(solidProgram, "u_NormalMatrix"); //获取纹理立方体的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"); if(solidProgram.a_Position < 0 || solidProgram.a_Normal < 0 || !solidProgram.u_MvpMatrix || !solidProgram.u_NormalMatrix || textureProgram.a_Position < 0 || textureProgram.a_Normal < 0 || textureProgram.a_TexCoord < 0 || !textureProgram.u_MvpMatrix || !textureProgram.u_NormalMatrix || !textureProgram.u_Sampler){ console.log("无法获取变量的存储位置"); return; } //设置单色立方体的相关数据,并存入缓冲区 var cube = initVertexBuffers(gl); if(!cube){ console.log("无法设置单色立方体的相关信息"); return; } //设置纹理立方体的纹理数据,并存入缓冲区 var texture = initTextures(gl,textureProgram); if(!texture){ console.log("纹理数据存储失败"); return; } //开启隐藏面消除功能,并设置背景色 gl.enable(gl.DEPTH_TEST); gl.clearColor(0.0, 0.0, 0.0, 1.0); //计算视图投影矩阵 var viewProjectMatrix = new Matrix4(); viewProjectMatrix.setPerspective(30.0, canvas.width/canvas.height, 1.0, 100.0); viewProjectMatrix.lookAt(0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); //开始绘制立方体 var currentAngle = 0.0; // 当前立方体的角度 function tick() { currentAngle = animate(currentAngle); //更新角度 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); //绘制单色立方体 drawSolidCube(gl, solidProgram, cube, -2.0, currentAngle, viewProjectMatrix); //绘制纹理立方体 drawTexCube(gl, textureProgram, cube, texture, 2.0, currentAngle, viewProjectMatrix); requestAnimationFrame(tick); } tick(); } function drawTexCube(gl, program, obj, texture, x, angle, viewProjectMatrix) { gl.useProgram(program); //分配缓冲对象并启用赋值 initAttributeVariable(gl, program.a_Position, obj.vertexBuffer); //顶点坐标 initAttributeVariable(gl, program.a_Normal, obj.normalBuffer); //法向量 initAttributeVariable(gl, program.a_TexCoord, obj.texCoordBuffer); //纹理坐标 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.indexBuffer); //设置好纹理对象,开启使用0号的纹理 gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); //绘制单色立方体 drawCube(gl, program, obj, x, angle, viewProjectMatrix); } function drawSolidCube(gl, program, obj, x, angle, viewProjectMatrix) { gl.useProgram(program); //分配缓冲区对象并启用赋值 initAttributeVariable(gl, program.a_Position, obj.vertexBuffer); //顶点坐标 initAttributeVariable(gl, program.a_Normal, obj.normalBuffer); //法向量 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.indexBuffer); //绑定索引 //绘制单色立方体 drawCube(gl, program, obj, x, angle, viewProjectMatrix); } //声明绘制需要变换矩阵变量 var g_modelMatrix = new Matrix4(); var g_mvpMatrix = new Matrix4(); var g_normalMatrix = new Matrix4(); function drawCube(gl, program, obj, x, angle, viewProjectMatrix) { //计算模型矩阵 g_modelMatrix.setTranslate(x, 0.0, 0.0); g_modelMatrix.rotate(20.0, 1.0, 0.0, 0.0); g_modelMatrix.rotate(angle, 0.0, 1.0, 0.0); //计算出法向量的方向,并赋值 g_normalMatrix.setInverseOf(g_modelMatrix); g_normalMatrix.transpose(); gl.uniformMatrix4fv(program.u_NormalMatrix, false, g_normalMatrix.elements); //计算模型视图投影矩阵 g_mvpMatrix.set(viewProjectMatrix); g_mvpMatrix.multiply(g_modelMatrix); gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements); gl.drawElements(gl.TRIANGLES, obj.numIndices, obj.indexBuffer.type, 0); } function initAttributeVariable(gl,a_attribute, buffer) { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0); gl.enableVertexAttribArray(a_attribute); } var angle_step = 30; //每秒旋转的增量 var last = +new Date(); //保存上次调用animate()函数的方法 function animate(angle) { var now = +new Date(); var elapsed = now - last; last = now; var newAngle = angle + (angle_step * elapsed)/1000.0; return newAngle % 360.0; } function initTextures(gl,program) { var texture = gl.createTexture(); if(!texture){ console.log("无法创建纹理缓冲区"); return null; } var img = new Image(); img.onload = function () { //将图形数据存入纹理对象 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); //将纹理存入到第一个纹理缓冲区 gl.useProgram(program); gl.uniform1i(program.u_Sampler, 0); gl.bindTexture(gl.TEXTURE_2D, null); //解绑当前 }; img.src = './resources/orange.jpg'; return texture; } function initVertexBuffers(gl) { // Create a cube // v6----- v5 // /| /| // v1------v0| // | | | | // | |v7---|-|v4 // |/ |/ // v2------v3 var vertices = new Float32Array([ // 单色立方体的顶点位置数据 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, // v0-v1-v2-v3 front 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, // v0-v3-v4-v5 right 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, // v0-v5-v6-v1 up -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, // v1-v6-v7-v2 left -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // v7-v4-v3-v2 down 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0 // v4-v7-v6-v5 back ]); var normals = new Float32Array([ // 单色立方体的法相量 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back ]); var texCoords = new Float32Array([ // 纹理的坐标 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v1-v2-v3 front 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, // v0-v3-v4-v5 right 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // v0-v5-v6-v1 up 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v1-v6-v7-v2 left 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // v7-v4-v3-v2 down 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 // v4-v7-v6-v5 back ]); var indices = new Uint8Array([ // 索引值 0, 1, 2, 0, 2, 3, // front 4, 5, 6, 4, 6, 7, // right 8, 9, 10, 8, 10, 11, // up 12, 13, 14, 12, 14, 15, // left 16, 17, 18, 16, 18, 19, // down 20, 21, 22, 20, 22, 23 // back ]); var obj = {}; //使用对象返回多个缓冲区对象 //将顶点信息写入缓冲区对象 obj.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT); obj.normalBuffer = initArrayBufferForLaterUse(gl, normals, 3, gl.FLOAT); obj.texCoordBuffer = initArrayBufferForLaterUse(gl, texCoords, 2, gl.FLOAT); obj.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE); if(!obj.vertexBuffer || !obj.normalBuffer || !obj.texCoordBuffer || !obj.indexBuffer){ return null; } obj.numIndices = indices.length; //取消绑定焦点的数据 gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); return obj; } function initElementArrayBufferForLaterUse(gl,data,type) { var buffer = gl.createBuffer(); //创建一个缓冲区对象 if(!buffer){ console.log("无法创建缓冲区对象"); return; } //将数据写入缓冲区 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); buffer.type = type; return buffer; } function initArrayBufferForLaterUse(gl, data, num, type) { var buffer = gl.createBuffer(); // 创建一个缓冲区对象 if(!buffer){ console.log("无法创建缓冲区对象"); return; } //将数据写入缓冲区对象 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); //保留必要的信息,以便后面使用 buffer.num = num; buffer.type = type; return buffer; }</script></html>
- 51 WebGL切换着色器
- 05 WebGL初始化着色器
- WebGL中的OpenGL着色器语言
- WebGL笔记 第二章 着色器
- 04 WebGL绘图之着色器
- WebGL笔记_着色器(三)
- WebGL 着色器语言(GLSL ES)
- [WebGL入门]八,着色器的说明和基础
- [WebGL入门]十一,着色器的编译和连接
- WebGL-片元着色器 1.Bloom特效实现
- WebGL-片元着色器 2.灰度图实现
- WebGL学习系列-片元着色器简介
- 01 WebGL 着色器编程语言GLSL ES概述
- 06 WebGL 着色器编程语言GLSL ES的数组
- 10 WebGL 着色器编程语言GLSL ES的函数
- WebGL自学教程——关于WebGL着色器中的自定义函数
- 39 WebGL着色器和着色器程序对象:initShader()函数的作用
- 40 WebGL着色器和着色器程序对象:initShader()函数的内部流程
- React-Native中的Text
- [模板]KMP算法
- spring boot mybatis打印sql
- Nginx实现虚拟主机 练习总结2017-6-20
- Glide类似You cannot start a load for a destroyed activity异常简单分析
- 51 WebGL切换着色器
- java异常捕获的一点感悟
- Longest Increasing Subsequence
- openwrt 免密码登陆到其他系统
- Spark+scala+Idea wordcount 示例
- HOSTS
- OC基础-Objective-c 入门01
- 记一个html问题
- Java在特定区间产生随机数