1. varying变量
顶点着色器向片元着色器传递数据
(1) 流程介绍
const canvas = document.querySelector('#canvas')
const gl = canvas.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
varying vec4 vColor;
void main() {
vColor = aPosition;
gl_Position = aPosition;
}
` // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor; // vec4(1.0, 0.0, 0.0, 1.0)
}
` // 片元着色器
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
)
const aPosition = gl.getAttribLocation(program, 'aPosition')
// 创建三个点的数组
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0, 0.5,
])
// 创建缓冲区对象
const buffer = gl.createBuffer()
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
// 将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW)
// 获取数据字节数
const BYTES = points.BYTES_PER_ELEMENT
// 将数据写入缓冲区对象
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 2, 0)
// 启用 attribute 变量
gl.enableVertexAttribArray(aPosition)
gl.drawArrays(gl.TRIANGLES, 0, 3)
2. 从顶点到图形的渲染流程
- 配成几何图形: 将独立的顶点坐标装配成几何图形(gl.drawArrays)
- 光栅化: 将装配好的图形转换为片元
-
- 剔除: 对于不透明物体,背面对于观察者来说是不可见的。那么渲染过程中,就会将不可见的部分剔除,不参与绘制。节省渲染开销。
- 裁剪: 在可视范围之外的事物是看不到的。可视范围之外的部分被裁剪掉,不参与绘制。
- 绘制: 所有片元会进行片元着色器的处理,绘制到浏览器
3. 在VScode中用js写webgl显示代码提示
如果在document.getElementById的上一行加入
/** @type {HTMLCanvasElement} */
这样再看canvas的类型就显示出来了,也有代码补全功能。
另外如果在函数中想使用传入的参数,给这个参数加入代码提示,则在对应的函数前加上:
/** @param {!WebGLRenderingContext} gl */
这样在函数中使用gl的时候也会有正确的代码提示。
4. 给图形添加背景图
(1) 使用单个纹理
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')
const gl = canvas.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
// 只传递顶点数据
attribute vec4 aPosition;
attribute vec4 aTex;
varying vec2 vTex;
void main() {
gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
vTex = vec2(aTex.x, aTex.y);
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
uniform sampler2D uSampler;
varying vec2 vTex;
void main() {
gl_FragColor = texture2D(uSampler, vTex);
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aTex = gl.getAttribLocation(program, 'aTex');
const uSampler = gl.getUniformLocation(program, 'uSampler');
const points = new Float32Array([
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 1.0, 0.0,
])
const buffer = gl.createBuffer();
const BYTES = points.BYTES_PER_ELEMENT;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 4, 0);
gl.enableVertexAttribArray(aPosition)
gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, BYTES * 4, BYTES * 2);
gl.enableVertexAttribArray(aTex)
const img = new Image();
img.onload = function () {
// 创建纹理对象
const texture = gl.createTexture();
// 翻转 图片 Y轴
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_MAG_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
// 横向 纵向 平铺的方式
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
// 配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
gl.uniform1i(uSampler, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
img.src = ''
(2) 使用多重纹理
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')
const gl = canvas.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
// 只传递顶点数据
attribute vec4 aPosition;
attribute vec4 aTex;
varying vec2 vTex;
void main() {
gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)
vTex = vec2(aTex.x, aTex.y);
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
uniform sampler2D uSampler;
uniform sampler2D uSampler1;
varying vec2 vTex;
void main() {
vec4 c1 = texture2D(uSampler, vTex);
vec4 c2 = texture2D(uSampler1, vTex);
gl_FragColor = c1 * c2;
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aTex = gl.getAttribLocation(program, 'aTex');
const uSampler = gl.getUniformLocation(program, 'uSampler');
const uSampler1 = gl.getUniformLocation(program, 'uSampler1');
const points = new Float32Array([
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 1.0, 0.0,
])
const buffer = gl.createBuffer();
const BYTES = points.BYTES_PER_ELEMENT;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 4, 0);
gl.enableVertexAttribArray(aPosition)
gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, BYTES * 4, BYTES * 2);
gl.enableVertexAttribArray(aTex)
function getImage(location, imgUrl, index) {
return new Promise(resolve => {
const img = new Image();
img.onload = function () {
// 创建纹理对象
const texture = gl.createTexture();
// 翻转 图片 Y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)
// 开启一个纹理单元
gl.activeTexture(gl[`TEXTURE${index}`]);
// 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 处理放大缩小的逻辑
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
// 横向 纵向 平铺的方式
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
// 配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
gl.uniform1i(location, index);
resolve()
}
img.src = imgUrl
})
}
Promise.all([getImage(uSampler, imgUrl1, 0), getImage(uSampler1, imgUrl2, 1), ]).then(() => {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
})