一 裁剪坐标系、canvas坐标系以及屏幕坐标系
裁剪坐标(WebGL坐标系)的范围:(-1,1),原点在中间,x正值向右,y正值向上
屏幕坐标:原点在左上角,x正值向右,y正值向下
canvas坐标:与屏幕坐标相比,原点向右向下偏移,x,y正值方向不变
参考:
(26条消息) WebGL屏幕坐标系、canvas坐标系和WebGL坐标系转换——学习笔记_ponGISer的博客-CSDN博客https://blog.csdn.net/u011332271/article/details/110477155二 顶点着色器的修改
在前一例绘制三角形的过程当中,我们传入的顶点是裁剪空间坐标,而一般情况下,我们更希望传入屏幕像素坐标,因此需要将传入的像素坐标转换成裁剪坐标。
<script id="vertex-shader" type="nojs">
attribute vec2 a_position;
uniform vec2 u_resolution;
void main(){
//将像素坐标转换为0~1,u_resolution是画布的分辨率
vec2 zeroToOne = a_position / u_resolution;
//再转换为0~2
vec2 zeroToTwo = zeroToOne * 2.0;
//再转换为-1~1(即裁剪空间坐标)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace,0,1);
}
</script>
三 封装编译着色器、连接着色器程序的函数(有专门的webUtils工具库对这两个操作进行封装,webgl-utils.js)
//创建着色器并编译
function createShaders(webgl, type, source) {
var shader = webgl.createShader(type);
webgl.shaderSource(shader, source);
webgl.compileShader(shader);
var success = webgl.getShaderParameter(shader, webgl.COMPILE_STATUS);
if (!success) {
console.log("error:", webgl.getShaderInfoLog(shader));
return;
}
return shader;
}
//创建着色程序并链接
function createPrograms(webgl, vertexShader, fragmentShader) {
var program = webgl.createProgram();
webgl.attachShader(program, vertexShader);
webgl.attachShader(program, fragmentShader);
webgl.linkProgram(program);
var success = webgl.getProgramParameter(program, webgl.LINK_STATUS);
if (!success) {
console.log("error:", webgl.getProgramInfoLog(program));
return;
}
return program;
}
四 绘制矩形(需要绘制两个三角形)
var positionAttributeLocation = webgl.getAttribLocation(programs, "a_position");
var resolutionUniformLocation = webgl.getUniformLocation(programs, "u_resolution");
var positionBuffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, positionBuffer);
//矩形由两个三角形构成
var positions = [
10, 20,
80, 20,
10, 30,
10, 30,
80, 20,
80, 30
]
webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(positions), webgl.STATIC_DRAW);
webgl.viewport(0, 0, webgl.canvas.width, webgl.canvas.height);
webgl.clearColor(0, 0, 0, 1);
webgl.clear(webgl.COLOR_BUFFER_BIT);
webgl.useProgram(programs);
//attribute
webgl.enableVertexAttribArray(positionAttributeLocation);
webgl.bindBuffer(webgl.ARRAY_BUFFER, positionBuffer);
webgl.vertexAttribPointer(positionAttributeLocation, 2, webgl.FLOAT, false, 0, 0);
//uniform
webgl.uniform2f(resolutionUniformLocation, webgl.canvas.width, webgl.canvas.height);
webgl.drawArrays(webgl.TRIANGLES, 0, 6);
结果如下:
可以看到矩形在右下角,而不是我们所认为的在左上角,从屏幕坐标与webgl坐标我们可以看出它们y轴的正方向不同,因此在转换成裁剪坐标之后我们需要对y轴进行一次翻转即可,顶点着色器程序就变成了这样:
<script id="vertex-shader" type="nojs">
attribute vec2 a_position;
uniform vec2 u_resolution;
void main(){
//将像素坐标转换为0~1,u_resolution是画布的分辨率
vec2 zeroToOne = a_position / u_resolution;
//再转换为0~2
vec2 zeroToTwo = zeroToOne * 2.0;
//再转换为-1~1(即裁剪空间坐标)
vec2 clipSpace = zeroToTwo - 1.0;
//翻转y轴
gl_Position = vec4(clipSpace * vec2(1,-1),0,1);
}
</script>
结果如下:
六 自定义颜色
颜色是在片段着色器进行修改的,想要自定义颜色的话就需要利用uniform变量进行自定义传值。片段着色器代码如下:
<script id="fragment-shader" type="nojs">
precision mediump float;
uniform vec4 u_color;
void main(){
gl_FragColor = u_color;
}
</script>
定义uniform变量主要包括两个步骤:
1. 获取uniform变量的位置:
var colorUniformLocation = webgl.getUniformLocation(programs, "u_color");
2.为它定义一个值(由于颜色值我们在着色器中定义的是vec4,因此也就需要4个参数即rbga):
webgl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1);
结果如下(每次刷新都会得到一个颜色不一样的矩形):
七 一点点不一样的小拓展:绘制50个颜色不一大小不一的随机矩形:
注意在渲染前一定要做好准备工作噢(比如创建缓冲区、绑定缓冲、存放数据、启用缓冲区以及说明绘制方式等)
for (var ii = 0; ii < 50; ii++) {
setRectangle(webgl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));
webgl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1);
webgl.drawArrays(webgl.TRIANGLES, 0, 6);
}
function randomInt(range) {
return Math.floor(Math.random() * range);
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2
]), gl.STATIC_DRAW);
}
结果如下: