Nic*_*kyP 2 mouse shader position webgl
我正在尝试使用WebGL产生有趣的效果。在片段着色器中,我有以下一行以黑白方式绘制我的纹理:
gl_FragColor = vec4(vec3(color0.r+color0.g+color0.b)/3.0, color0.a);
Run Code Online (Sandbox Code Playgroud)
color0纹理的颜色在哪里。在着色器中,我还具有一个uniform vec2 u_mouse从我的JavaScript代码传入的内容,作为鼠标在屏幕上的坐标。现在我想要的是能够移动鼠标,并且图像的一部分将在给定的半径内变色,如图所示:
我的想法是要有一个带有白色圆圈的蒙版,该蒙版会随鼠标移动,但是我不知道随后如何进行图像处理...我也希望动画不像鼠标值之间的插值。
谢谢!
您想将黑白版本与彩色版本混合使用
vec4 bw = vec4(vec3(color0.r + color0.g + color0.b) / 3., color.a);
gl_FragColor = mix(bw, color0, mixAmount);
Run Code Online (Sandbox Code Playgroud)
哪里mix定义为
mix(a, b, l) = a + (b - a) * l
Run Code Online (Sandbox Code Playgroud)
换句话说,如果mixAmount为0,您将得到bw;如果mixAmount为1,您将得到color0。对于介于0和1之间的值,您将混合使用2。
所以现在您只需要一些公式 setting mixAmount
作为一个示例,假设您在画布的相对坐标中传递鼠标,则可以计算与该坐标的距离
uniform vec2 mousePos; // in pixels where 0,0 is bottom left
...
float dist = distance(mousePos, gl_FragCoord.xy);
Run Code Online (Sandbox Code Playgroud)
然后,您可以用它来计算mixAmount,例如
uniform float mixRadius;
float mixAmount = clamp(dist / mixRadius, 0., 1.);
Run Code Online (Sandbox Code Playgroud)
然后您将得到一个渐变色的圆圈,其中心颜色变为边缘的黑色和白色。
如果您希望中央的较大区域成为彩色,则可以传入minRadius和maxRadius
uniform float minRadius;
uniform float maxRadius;
float range = maxRadius - minRadius
float mixAmount = clamp((dist - minRadius) / range, 0., 1.);
Run Code Online (Sandbox Code Playgroud)
或类似的东西
这是一个有效的例子
vec4 bw = vec4(vec3(color0.r + color0.g + color0.b) / 3., color.a);
gl_FragColor = mix(bw, color0, mixAmount);
Run Code Online (Sandbox Code Playgroud)
mix(a, b, l) = a + (b - a) * l
Run Code Online (Sandbox Code Playgroud)
uniform vec2 mousePos; // in pixels where 0,0 is bottom left
...
float dist = distance(mousePos, gl_FragCoord.xy);
Run Code Online (Sandbox Code Playgroud)
就像您提到的那样,您还可以传递蒙版纹理。这将使您可以轻松制作其他形状。同样,您只需要一个值mixAmount
所以,像
uniform mat4 maskMatrix;
...
vec2 maskUV = (maskMatrix * vec4(v_texcoord, 0, 1)).xy;
float mixAmount = texture2D(mask, maskUV).a;
Run Code Online (Sandbox Code Playgroud)
通过阅读以下文章,您可以了解如何使用2d或3d矩阵设置该矩阵
uniform float mixRadius;
float mixAmount = clamp(dist / mixRadius, 0., 1.);
Run Code Online (Sandbox Code Playgroud)
uniform float minRadius;
uniform float maxRadius;
float range = maxRadius - minRadius
float mixAmount = clamp((dist - minRadius) / range, 0., 1.);
Run Code Online (Sandbox Code Playgroud)
"use strict";
const vs = `
attribute vec4 position;
attribute vec2 texcoord;
uniform mat4 matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
v_texcoord = texcoord;
}
`;
const fs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D tex;
uniform vec2 mousePos;
uniform float minRadius;
uniform float maxRadius;
void main() {
vec4 color0 = texture2D(tex, v_texcoord);
vec4 bw = vec4(vec3(color0.r + color0.g + color0.b) / 3., color0.a);
float dist = distance(mousePos, gl_FragCoord.xy);
float range = maxRadius - minRadius;
float mixAmount = clamp((dist - minRadius) / range, 0., 1.);
gl_FragColor = mix(color0, bw, mixAmount);
}
`;
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const info = document.querySelector("#info");
// compiles shaders, link program, looks up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
const bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
const textureInfo = {
width: 1,
height: 1,
};
const texture = twgl.createTexture(gl, {
src: "http://i.imgur.com/NzBzAdN.jpg",
crossOrigin: '',
flipY: true,
}, (err, tex, img) => {
textureInfo.width = img.width;
textureInfo.height = img.height;
render();
});
const mousePos = [0, 0];
function render() {
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// cover canvas with image
const canvasAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const imageAspect = textureInfo.width / textureInfo.height;
// this assumes we want to fill vertically
let horizontalDrawAspect = imageAspect / canvasAspect;
let verticalDrawAspect = 1;
// does it fill horizontally?
if (horizontalDrawAspect < 1) {
// no it does not so scale so we fill horizontally and
// adjust vertical to match
verticalDrawAspect /= horizontalDrawAspect;
horizontalDrawAspect = 1;
}
const mat = m4.scaling([horizontalDrawAspect, verticalDrawAspect, 1]);
// calls gl.activeTexture, gl.bindTexture, gl.uniform
twgl.setUniforms(programInfo, {
minRadius: 25,
maxRadius: 100,
tex: texture,
matrix: mat,
mousePos: mousePos,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
render();
gl.canvas.addEventListener('mousemove', e => {
const canvas = e.target;
const rect = canvas.getBoundingClientRect();
const x = (e.clientX - rect.left) * canvas.width / rect.width;
const y = (e.clientY - rect.top) * canvas.height / rect.height;
mousePos[0] = x;
mousePos[1] = canvas.height - y - 1;
render();
});
window.addEventListener('resize', render);Run Code Online (Sandbox Code Playgroud)
注意我使用F带有框架的a来清楚地显示蒙版。还要注意,必须将蒙版的边缘保持为0,因为边缘像素将重复经过蒙版的边界。否则,或者当与蒙版一起使用的纹理坐标为<0或> 1时,需要将着色器修改为使用0。
我还使用矩阵来操纵UV坐标。由于它是矩阵,因此无需更改着色器即可轻松缩放,偏移和/或旋转蒙版。
至于动画,还不清楚您想要哪种动画。如果您想要某种颜色随时间逐渐消失的东西,可以使用此回答中的技术。您将在另一对纹理中绘制蒙版。您可以使用那对纹理作为mixAmount蒙版。通过将一个纹理绘制到另一个纹理中,每帧减去一定量,可以使这些纹理退回到0
gl_FragColor = texture2D(mixTexture, uv).rgba - vec4(0.01);
Run Code Online (Sandbox Code Playgroud)
例如。
| 归档时间: |
|
| 查看次数: |
1701 次 |
| 最近记录: |