WebGL/GLSL 类似于ShaderToy的时间变量
WebGL/GLSL time variable similar to ShaderToy
这是我的顶点着色器
attribute vec4 a_position;
varying vec4 v_color;
void main() {
gl_Position = vec4(a_position.xy, 0.0, 1.0);
v_color = gl_Position * 0.5 + 0.5;
}
这是我的片段着色器
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
这是我的 JS,它设置了一切
var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader = createShader(gl, gl.VERTEX_SHADER, window.vert);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, window.frag);
var program = createProgram(gl, vertexShader, fragmentShader);
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, 1,
1, 1,
1, -1,
-1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
resizeCanvas(gl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset)
// // draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
我得到以下输出
但我接下来想要实现的是使用类似于 ShaderToy 的 iTime
的一些时间变量对此进行动画处理。
如何设置这样的东西?
您应该将一些时间变量作为uniform传递给着色器并使用它来创建一些动画效果。使用 requestAnimationFrame
获得快速高效的动画。
const vert = `
attribute vec4 a_position;
varying vec4 v_color;
void main() {
gl_Position = vec4(a_position.xy, 0.0, 1.0);
v_color = gl_Position * 0.5 + 0.5;
}`
const frag = `
precision mediump float;
varying vec4 v_color;
uniform float time;
void main() {
gl_FragColor = v_color;
gl_FragColor.r = v_color.r * 0.5 * (1.0 + sin(4.0*time) );
gl_FragColor.g = v_color.g * 0.5 * (1.0 + sin(1.0 + 2.0*time) );
}`
function animate(t) {
gl.uniform1f(timeUniformLocation, t / 1000); // convert from milis to seconds
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(primitiveType, offset, count);
requestAnimationFrame(animate)
}
var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader =gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader,vert);
gl.compileShader(vertexShader);
var fragmentShader =gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader,frag);
gl.compileShader(fragmentShader);
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log(gl.getShaderInfoLog(fragmentShader))
console.log(gl.getShaderInfoLog(vertexShader))
}
var timeUniformLocation = gl.getUniformLocation(program, "time");
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, 1,
1, 1,
1, -1,
-1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset)
// // draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
animate()
<canvas id="canvas" width="400px" height="300px"></canvas>
如果你想传递时间你只需要制作一个制服来保持时间并将其设置为某个时间或计数器
uniform float time;
在JavaScript中查找初始时间的位置
const timeLocation = gl.getUniformLocation(program, "time");
在渲染时设置它
gl.uniform1f(timeLocation, someTimeValue);
您还需要使用 requestAnimationFrame
的渲染循环。 requestAnimationFrame
页面加载后的时间已过去,因此您可以直接使用它
function render(time) {
...
gl.useProgram(program);
gl.uniform1f(timeLocation, time * 0.001); // time in seconds
...
// draw
requestAnimationFrame(render);
}
requestAnimationFrame(render);
示例:
var vert = `
attribute vec4 a_position;
varying vec4 v_color;
void main() {
gl_Position = vec4(a_position.xy, 0.0, 1.0);
v_color = gl_Position * 0.5 + 0.5;
}
`;
var frag = `
precision mediump float;
varying vec4 v_color;
uniform float time;
void main() {
gl_FragColor = vec4(fract(v_color.rgb + time), 1);
}
`;
var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader = createShader(gl, gl.VERTEX_SHADER, window.vert);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, window.frag);
var program = createProgram(gl, vertexShader, fragmentShader);
const timeLocation = gl.getUniformLocation(program, "time");
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, 1,
1, 1,
1, -1,
-1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
function render(time) {
resizeCanvas(gl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset)
gl.uniform1f(timeLocation, time * 0.001);
// // draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function resizeCanvas(gl) {
// not important for example
}
function createProgram(gl, vs, fs) {
const p = gl.createProgram();
gl.attachShader(p, vs);
gl.attachShader(p, fs);
gl.linkProgram(p);
// should check for error here!
return p;
}
function createShader(gl, type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
// should check for error here
return s;
}
<canvas id="canvas"></canvas>
这是我的顶点着色器
attribute vec4 a_position;
varying vec4 v_color;
void main() {
gl_Position = vec4(a_position.xy, 0.0, 1.0);
v_color = gl_Position * 0.5 + 0.5;
}
这是我的片段着色器
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
这是我的 JS,它设置了一切
var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader = createShader(gl, gl.VERTEX_SHADER, window.vert);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, window.frag);
var program = createProgram(gl, vertexShader, fragmentShader);
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, 1,
1, 1,
1, -1,
-1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
resizeCanvas(gl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset)
// // draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
我得到以下输出
但我接下来想要实现的是使用类似于 ShaderToy 的 iTime
的一些时间变量对此进行动画处理。
如何设置这样的东西?
您应该将一些时间变量作为uniform传递给着色器并使用它来创建一些动画效果。使用 requestAnimationFrame
获得快速高效的动画。
const vert = `
attribute vec4 a_position;
varying vec4 v_color;
void main() {
gl_Position = vec4(a_position.xy, 0.0, 1.0);
v_color = gl_Position * 0.5 + 0.5;
}`
const frag = `
precision mediump float;
varying vec4 v_color;
uniform float time;
void main() {
gl_FragColor = v_color;
gl_FragColor.r = v_color.r * 0.5 * (1.0 + sin(4.0*time) );
gl_FragColor.g = v_color.g * 0.5 * (1.0 + sin(1.0 + 2.0*time) );
}`
function animate(t) {
gl.uniform1f(timeUniformLocation, t / 1000); // convert from milis to seconds
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(primitiveType, offset, count);
requestAnimationFrame(animate)
}
var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader =gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader,vert);
gl.compileShader(vertexShader);
var fragmentShader =gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader,frag);
gl.compileShader(fragmentShader);
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log(gl.getShaderInfoLog(fragmentShader))
console.log(gl.getShaderInfoLog(vertexShader))
}
var timeUniformLocation = gl.getUniformLocation(program, "time");
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, 1,
1, 1,
1, -1,
-1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset)
// // draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
animate()
<canvas id="canvas" width="400px" height="300px"></canvas>
如果你想传递时间你只需要制作一个制服来保持时间并将其设置为某个时间或计数器
uniform float time;
在JavaScript中查找初始时间的位置
const timeLocation = gl.getUniformLocation(program, "time");
在渲染时设置它
gl.uniform1f(timeLocation, someTimeValue);
您还需要使用 requestAnimationFrame
的渲染循环。 requestAnimationFrame
页面加载后的时间已过去,因此您可以直接使用它
function render(time) {
...
gl.useProgram(program);
gl.uniform1f(timeLocation, time * 0.001); // time in seconds
...
// draw
requestAnimationFrame(render);
}
requestAnimationFrame(render);
示例:
var vert = `
attribute vec4 a_position;
varying vec4 v_color;
void main() {
gl_Position = vec4(a_position.xy, 0.0, 1.0);
v_color = gl_Position * 0.5 + 0.5;
}
`;
var frag = `
precision mediump float;
varying vec4 v_color;
uniform float time;
void main() {
gl_FragColor = vec4(fract(v_color.rgb + time), 1);
}
`;
var gl = document.getElementById("canvas").getContext('webgl');
var vertexShader = createShader(gl, gl.VERTEX_SHADER, window.vert);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, window.frag);
var program = createProgram(gl, vertexShader, fragmentShader);
const timeLocation = gl.getUniformLocation(program, "time");
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, 1,
1, 1,
1, -1,
-1, -1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
function render(time) {
resizeCanvas(gl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset)
gl.uniform1f(timeLocation, time * 0.001);
// // draw
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function resizeCanvas(gl) {
// not important for example
}
function createProgram(gl, vs, fs) {
const p = gl.createProgram();
gl.attachShader(p, vs);
gl.attachShader(p, fs);
gl.linkProgram(p);
// should check for error here!
return p;
}
function createShader(gl, type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
// should check for error here
return s;
}
<canvas id="canvas"></canvas>