我渲染网格纹理。我想在片段着色器中操纵uv坐标(vQuadCoord
)来产生黑洞效果,即使线之间的间隙在接近中心时更远。也有圆形效果
我认为这是有可能的,因为如果我vQuadCoord = vQuadCoord * vQuadCoord
这样做会达到类似的效果,但是在角落。
const fShaderSource = `#version 300 es
precision mediump float;
out vec4 outColor;
uniform sampler2D u_texture;
in vec2 vQuadCoord;
void main() {
outColor = texture(u_texture, vQuadCoord);
}
`;
const vShaderSource = `#version 300 es
precision mediump float;
in vec2 a_position;
out vec2 vQuadCoord;
void main() {
vQuadCoord = (a_position + 1.0) / 2.0;
gl_Position = vec4(a_position, 0, 1);
}
`;
main(document.getElementById('app'));
function main(element) {
const canvas = document.createElement('canvas'),
gl = canvas.getContext('webgl2');
element.append(canvas);
const displayWidth = canvas.clientWidth,
displayHeight = canvas.clientHeight;
canvas.width = displayWidth;
canvas.height = displayHeight;
let graphics = new Graphics({width: displayWidth, height: displayHeight}, gl);
new Loop(() => {
graphics.render();
}).start();
}
function Graphics(state, gl) {
const { width, height } = state;
gl.clearColor(0, 0, 0, 0);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
//gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
gl.enable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
let minibatch = [];
const redText = makeGlQuad(gl, fShaderSource, canvasTexture());
this.render = () => {
minibatch.push(redText);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
minibatch.forEach(({
program,
resUniformLocation,
vao,
glTexture
}) => {
gl.useProgram(program);
gl.uniform2f(resUniformLocation, gl.canvas.width, gl.canvas.height);
if (glTexture) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, glTexture);
}
gl.bindVertexArray(vao);
gl.drawArrays(gl.TRIANGLES, 0, 6);
});
minibatch = [];
};
}
function makeGlQuad(gl, fShaderSource, texture) {
let vShader = createShader(gl, gl.VERTEX_SHADER, vShaderSource);
let fShader = createShader(gl, gl.FRAGMENT_SHADER, fShaderSource);
let program = createProgram(gl, vShader, fShader);
let posAttrLocation = gl.getAttribLocation(program, "a_position");
let posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
let left = -1,
right = 1,
down = -1,
up = 1;
/*
(-1, 1).( 1, 1)
.
(-1,-1).( 1,-1)
*/
let positions = [
left, down,
left, up,
right, down,
left, up,
right, down,
right, up
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(posAttrLocation);
let size = 2,
type = gl.FLOAT,
normalize = false,
stride = 0,
offset = 0;
gl.vertexAttribPointer(posAttrLocation,
size,
type,
normalize,
stride,
offset);
let glTexture;
if (texture) {
glTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture);
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
let resUniformLocation = gl.getUniformLocation(program, "u_resolution");
let texUniformLocation = gl.getUniformLocation(program, "u_texture");
return {
program,
resUniformLocation,
vao,
glTexture
}
}
function canvasTexture() {
return withCanvasTexture(256, 256, (w, h, canvas, ctx) => {
const gap = w * 0.07;
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
ctx.strokeStyle = 'red';
ctx.lineWidth = 1;
ctx.beginPath();
for (let i = 0; i < w; i+= gap) {
ctx.moveTo(i, 0);
ctx.lineTo(i, h);
}
for (let i = 0; i < h; i+= gap) {
ctx.moveTo(0, i);
ctx.lineTo(w, i);
}
ctx.stroke();
return canvas;
});
function withCanvasTexture(width, height, f) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
f(width, height, canvas, canvas.getContext('2d'));
const texture = canvas;
document.body.append(canvas);
return texture;
}
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
};
function createProgram(gl, vShader, fShader) {
let program = gl.createProgram();
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.error(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
// Loop Library
function Loop(fn) {
const perf = window.performance !== undefined ? window.performance : Date;
const now = () => perf.now();
const raf = window.requestAnimationFrame;
let running = false,
lastUpdate = now(),
frame = 0;
this.start = () => {
if (running) {
return this;
}
running = true;
lastUpdate = now();
frame = raf(tick);
return this;
};
this.stop = () => {
running = false;
if (frame != 0) {
raf.cancel(frame);
}
frame = 0;
return this;
};
const tick = () => {
frame = raf(tick);
const time = now();
const dt = time - lastUpdate;
fn(dt);
lastUpdate = time;
};
}
#app canvas {
background: #ccc;
position: fixed;
top: 50%;
bottom: 0;
left: 50%;
right: 0;
width: 100vmin;
height: 70vmin;
transform: translate(-50%, -25%);
image-rendering: optimizeSpeed;
cursor: none;
margin: auto;
}
<div id="app">
</div>
答案 0 :(得分:0)
[...]在片段着色器中产生黑洞效果,即使线之间的间隙在靠近中心时更远。
您必须执行类似(1.0 - (1.0 - abs(x)) * (1.0 - abs(x)))
的操作。 x
是一个坐标,其中(0,0)在纹理的中心。
将纹理坐标从[0,1]范围转换为[-1,1]范围:
vec2 p = vQuadCoord * 2.0 - 1.0;
计算“黑洞效应”坐标:
p = sign(p) * (1.0 - (1.0 - abs(p)) * (1.0 - abs(p)));
从[-1,1]范围转换回[0,1]:
vec2 uv = p * 0.5 + 0.5;
对于圆形效果,您必须将归一化方向向量乘以一个系数,该系数取决于到中心的平方距离或到边界的距离:
p = normalize(p) * length(p) * length(p);
或
p = normalize(p) * (1.0 - (1.0 - length(p)) * (1.0 - length(p)))
片段着色器:
precision mediump float;
out vec4 outColor;
uniform sampler2D u_texture;
in vec2 vQuadCoord;
void main() {
vec2 p = vQuadCoord * 2.0 - 1.0;
//p = sign(p) * (1.0 - (1.0 - abs(p)) * (1.0 - abs(p)));
//p = normalize(p) * (1.0 - (1.0 - length(p)) * (1.0 - length(p)));
p = normalize(p) * length(p) * length(p);
vec2 uv = p * 0.5 + 0.5;
outColor = texture(u_texture, uv);
}
要y翻转纹理,可以设置UNPACK_FLIP_Y_WEBGL
标志。参见WebGL 2.0, 5.14.8 Texture objects:
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture);