在JavaScript程序通过uniform变量向片元着色器传值
1.demo效果
如上图,这个demo实现在黑色区域内点用鼠标点击,会在不同的区域画出不同颜色的小方块,第一象限的画红色方块,第三象限画绿色方块,第二、四象限画蓝色方块。
2.相关知识点
2.1 片元着色器中的uniform变量
uniform变量是一种被用来从外部着色器传输数据的变量。先介绍一下uniform变量的声明,该变量的声明有一定的格式要求:<存储限定符><类型><变量名>
友情提示:
- uniform变量必须声明成全局变量
- 所有的uniform变量最好以u_前缀开始,这样从变量的名字就可以区分出类型
2.2 gl.getUniformLocation()方法
与attribute变量一样,要在JavaScript程序中使用着色器中的变量,首先要获取该变量的存储地址,attribute变量通过gl.getAttribLocation() 方法获取,uniform变量则通过gl.getUniformLocation() 方法获取
函数功能:获取由name指定的uniform变量的存储地址
--------------------------------------------------------------------------
调用示例:gl.getUniformLocation(program, name)
--------------------------------------------------------------------------
参数
program 指定包含顶点着色器和片元着色器的着色器程序对象
name 指定想要获取其存储地址的uniform变量名称
--------------------------------------------------------------------------
返回值 non-null uniform变量的存储地址
null 指定的uniform变量不存在,或命名具有gl_或webgl_前缀
--------------------------------------------------------------------------
错 误 INVALID_OPERATION 程序对象未能连接成功
INVALID_VALUE name参数的长度大于uniform变量名的最大长度,默认256字节
示例代码
//获取片元着色器uniform变量u_FragColor的存储地址
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
2.3 gl.uniform4f()方法
gl.getUniformLocation() 方法帮我们获取到着色器uniform变量的地址,我们在JavaScript程序中处理完,需要把新的值再传给这个uniform变量。这时就需要用到gl.uniform4f() 方法了
函数功能:将数据v0, v1, v2, v3 传给由location参数指定的uniform变量
---------------------------------------------------------------------------------
调用示例:gl.uniform4f(location, v0, v1, v2, v3)
---------------------------------------------------------------------------------
参数
location 指定将要修改的uniform变量的存储地址
v0 指定填充uniform变量第一个分量的值
v1 指定填充uniform变量第二个分量的值
v2 指定填充uniform变量第三个分量的值
v2 指定填充uniform变量第四个分量的值
---------------------------------------------------------------------------------
返回值 无
---------------------------------------------------------------------------------
错 误 INVALID_OPERATION 没有当前的program对象,或者location是非法的存储位置
示例代码
//向片元着色器uniform变量u_FragColor传值
gl.uniform4f(u_FragColor, 1.0, 0.0, 0.0, 1.0)
2.4 gl.uniform4f()的同族函数
gl.uniform4f() 方法是一系列同族函数中的一个,该系列函数的任务就是从JavaScript程序中向着色器中的uniform变量传值
函数功能:将数据[1~4个分量]传给由location参数指定的uniform变量
------------------------------------------------------------------------------
gl.uniform1f(location, v0)
gl.uniform2f(location, v0, v1)
gl.uniform3f(location, v0, v1, v2)
gl.uniform4f(location, v0, v1, v2, v3)
------------------------------------------------------------------------------
参数
location 指定将要修改的uniform变量的存储地址
v0,v1,v2,v3 指定传输给uniform变量的四个分量的值
------------------------------------------------------------------------------
返回值 无
------------------------------------------------------------------------------
错 误 INVALID_OPERATION 没有当前的program对象,或者location是非法的存储位置
友情提示:
gl.uniform1f()只需要传输一个值,这个值将被填充到uniform变量的第一个分量中,第二、第三个分量将被设置为0.0,第四个分量将被设置为1.0,其他同族函数类推
2.5 uniform变量使用流程介绍
uniform变量的使用大致流程为:
- 在着色器中声明uniform变量
- 在JavaScript程序中获取uniform变量的存储地址
- 在JavaScript程序中通过gl.uniform4f()同族函数向着色器中的uniform变量传值
以下是示意图
2.6 注册鼠标点击响应事件
在JavaScript程序中我们已经获取到canvas对象的实例,我们就可以把鼠标点击事件与他绑定,即把鼠标点击响应事件注册到canvas的onmousedown事件上。示例如下
//注册鼠标点击响应事件
canvas.onmousedown = function (ev) {
click(ev, gl, canvas, a_Position, u_FragColor)
}
// 定义点击事件的处理函数
function click(ev, gl, canvas, a_Position, u_FragColor) {
...
}
我们使用以上示例中的方式注册响应事件。而没有直接在canvas.onmousedown事件的匿名函数中处理响应逻辑,是因为匿名函数只有一个ev参数,而逻辑处理需要的gl, canvas, a_Position, u_FragColor参数无法改函数中直接使用,于是我们在canvas.onmousedown的匿名函数中调用了自己定义的click函数,传入了需要要到的参数。
3.demo代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<!--通过canvas标签创建一个400px*400px大小的画布-->
<canvas id="webgl" width="400" height="400"></canvas>
<script>
//顶点着色器
var VSHADER_SOURCE = '' +
'attribute vec4 a_Position;\\n' + //声明attribute变量a_Position,用来存放顶点位置信息
'attribute float a_PointSize;\\n' + //声明attribute变量a_PointSize,用来存放顶点大小信息
'void main(){\\n' +
' gl_Position = a_Position;\\n' + //变量a_Position赋值给顶点着色器内置变量gl_Position
' gl_PointSize = a_PointSize;\\n' + //变量a_PointSize赋值给顶点着色器内置变量gl_PointSize
'}\\n';
//片元着色器
var FSHADER_SOURCE = '' +
'precision mediump float;\\n' + // 设置精度
'uniform vec4 u_FragColor;\\n' + //声明uniform变量u_FragColor,用来存放顶点颜色信息
'void main(){\\n' +
//通过u_FragColor变量设置片元颜色
' gl_FragColor = u_FragColor;\\n' +
'}\\n';
//初始化着色器函数
function initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE) {
//创建顶点着色器对象
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
//创建片元着色器对象
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
//引入顶点、片元着色器源代码
gl.shaderSource(vertexShader, VSHADER_SOURCE);
gl.shaderSource(fragmentShader, FSHADER_SOURCE);
//编译顶点、片元着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
//创建程序对象program
var program = gl.createProgram();
//附着顶点着色器和片元着色器到program
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
//链接program
gl.linkProgram(program);
//使用program
gl.useProgram(program);
gl.program = program
//返回程序program对象
return program;
}
function init() {
//通过getElementById()方法获取canvas画布
var canvas = document.getElementById('webgl');
//通过方法getContext()获取WebGL上下文
var gl = canvas.getContext('webgl');
//初始化着色器
initShader(gl, VSHADER_SOURCE, FSHADER_SOURCE);
//获取着色器attribute变量a_Position的存储地址
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
//向着色器attribute变量a_Position传值
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)
//获取着色器attribute变量a_PointSize的存储地址
var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
//向顶点着色器attribute变量a_PointSize传值
gl.vertexAttrib1f(a_PointSize, 10.0)
//获取片元着色器uniform变量u_FragColor的存储地址
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
// 设置canvas的背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
//注册鼠标点击响应事件
canvas.onmousedown = function (ev) {
click(ev, gl, canvas, a_Position, u_FragColor)
}
}
var g_points = []
var g_colors = []
// 定义点击事件的处理函数
function click(ev, gl, canvas, a_Position, u_FragColor) {
var x = ev.clientX; // 浏览器中鼠标点击的x坐标
var y = ev.clientY; // 浏览器中鼠标点击的y坐标
//将浏览器的坐标系转换成WebGL坐标系
var rect = ev.target.getBoundingClientRect()
x = ((x - rect.left) - canvas.height / 2) / (canvas.height / 2)
y = (canvas.width / 2 - (y - rect.top)) / (canvas.width / 2)
// 将点的位置信息存储到g_points中
g_points.push([x, y])
// 将点的颜色信息存储到g_colors中
if (x >= 0.0 && y >= 0.0) { //第一象限
g_colors.push([1.0, 0.0, 0.0, 1.0]) //红色
} else if (x < 0.0 && y < 0.0) { //第三象限
g_colors.push([0.0, 1.0, 0.0, 1.0]) //绿色
} else { //第二、四象限
g_colors.push([0.0, 0.0, 1.0, 1.0]) //蓝色
}
//清空canvas
gl.clear(gl.COLOR_BUFFER_BIT)
var len = g_points.length
for (var i = 0; i < len; i++) {
//获取当前点的颜色数组
var rgba = g_colors[i]
//向顶点着色器attribute变量a_Position传值
gl.vertexAttrib3f(a_Position, g_points[i][0], g_points[i][1], 0.0)
//向片元着色器uniform变量u_FragColor传值
gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3])
//绘制点
gl.drawArrays(gl.POINTS, 0, 1)
}
}
init()
</script>
</body>
</html>
暂无评论内容