问题描述
这是我当前的结果:
目标结果(以及以下几行):
这是我的片段着色器:
precision highp float;
varying vec2 vTextureCoord;
uniform float centerX;
uniform float centerY;
uniform vec4 colors[4];
uniform float steps[4];
void main() {
vec3 map = vec3(vTextureCoord,1);
vec2 uv = map.xy;
float dist = distance(vTextureCoord,vec2(centerX,centerY));
highp vec4 col = colors[0];
for (int i = 1; i < 4; ++i) {
col = mix(col,colors[i],smoothstep(steps[i - 1],steps[i],dist));
}
float factor = max(abs(uv.x - centerX),abs(uv.y - centerY));
float c = 1. - max(abs(uv.x - centerX),abs(uv.y - centerY));
vec4 finalColor = vec4((col.r - factor),(col.g - factor),(col.b - factor),1.);
gl_FragColor = finalColor;
}
传递的参数是:
颜色:[[1、0、0],[1、1、1],[1、0、0],[0、1、0]]
步骤:[0,0.29,0.35,1]
解决方法
可以通过计算两个轴的最大距离来获得方形梯度:
float dist = distance(vTextureCoord,vec2(centerX,centerY));
vec2 distV = vTextureCoord - vec2(centerX,centerY);
float dist = max(abs(distV.x),abs(distV.y));
完整示例:
(function loadscene() {
var canvas,gl,vp_size,prog,bufObj = {};
function initScene() {
canvas = document.getElementById( "ogl-canvas");
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return;
progDraw = gl.createProgram();
for (let i = 0; i < 2; ++i) {
let source = document.getElementById(i==0 ? "draw-shader-vs" : "draw-shader-fs").text;
let shaderObj = gl.createShader(i==0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(shaderObj,source);
gl.compileShader(shaderObj);
let status = gl.getShaderParameter(shaderObj,gl.COMPILE_STATUS);
if (!status) alert(gl.getShaderInfoLog(shaderObj));
gl.attachShader(progDraw,shaderObj);
gl.linkProgram(progDraw);
}
status = gl.getProgramParameter(progDraw,gl.LINK_STATUS);
if ( !status ) alert(gl.getProgramInfoLog(progDraw));
progDraw.inPos = gl.getAttribLocation(progDraw,"inPos");
progDraw.u_time = gl.getUniformLocation(progDraw,"u_time");
progDraw.u_resolution = gl.getUniformLocation(progDraw,"u_resolution");
gl.useProgram(progDraw);
var pos = [ -1,-1,1,1 ];
var inx = [ 0,2,3 ];
bufObj.pos = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER,bufObj.pos );
gl.bufferData( gl.ARRAY_BUFFER,new Float32Array( pos ),gl.STATIC_DRAW );
bufObj.inx = gl.createBuffer();
bufObj.inx.len = inx.length;
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER,bufObj.inx );
gl.bufferData( gl.ELEMENT_ARRAY_BUFFER,new Uint16Array( inx ),gl.STATIC_DRAW );
gl.enableVertexAttribArray( progDraw.inPos );
gl.vertexAttribPointer( progDraw.inPos,gl.FLOAT,false,0 );
gl.enable( gl.DEPTH_TEST );
gl.clearColor( 0.0,0.0,1.0 );
window.onresize = resize;
resize();
requestAnimationFrame(render);
}
function resize() {
//vp_size = [gl.drawingBufferWidth,gl.drawingBufferHeight];
vp_size = [window.innerWidth,window.innerHeight];
//vp_size = [256,256]
canvas.width = vp_size[0];
canvas.height = vp_size[1];
}
function render(deltaMS) {
gl.viewport( 0,canvas.width,canvas.height );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
gl.uniform1f(progDraw.u_time,deltaMS/1000.0);
gl.uniform2f(progDraw.u_resolution,canvas.height);
gl.drawElements( gl.TRIANGLES,bufObj.inx.len,gl.UNSIGNED_SHORT,0 );
requestAnimationFrame(render);
}
initScene();
})();
<script id="draw-shader-vs" type="x-shader/x-vertex">
#version 100
attribute vec2 inPos;
varying vec2 ndcPos;
void main()
{
ndcPos = inPos;
gl_Position = vec4( inPos.xy,1.0 );
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 ndcPos; // normaliced device coordinates in range [-1.0,1.0]
uniform float u_time;
uniform vec2 u_resolution;
#define FILL
void main()
{
vec4 colors[4];
colors[0] = vec4(1.0,1.0);
colors[1] = vec4(0.0,1.0,1.0);
colors[2] = vec4(0.0,1.0);
colors[3] = vec4(1.0,1.0);
float steps[4];
steps[0] = 0.2;
steps[1] = 0.4;
steps[2] = 0.6;
steps[3] = 0.8;
vec2 uv = ndcPos.xy;
uv.x *= u_resolution.x / u_resolution.y;
vec2 vTextureCoord = uv;
float centerX = 0.0;
float centerY = 0.0;
//float dist = distance(vTextureCoord,centerY));
vec2 distV = vTextureCoord - vec2(centerX,centerY);
float dist = max(abs(distV.x),abs(distV.y));
highp vec4 col = colors[0];
for (int i = 1; i < 4; ++i) {
col = mix(col,colors[i],smoothstep(steps[i - 1],steps[i],dist));
}
float factor = max(abs(uv.x - centerX),abs(uv.y - centerY));
float c = 1. - max(abs(uv.x - centerX),abs(uv.y - centerY));
vec4 finalColor = vec4((col.r - factor),(col.g - factor),(col.b - factor),1.);
gl_FragColor = finalColor;
}
</script>
<canvas id="ogl-canvas" style="border: none"></canvas>
,
使用纹理坐标
下面是一个示例,该示例使用0-1范围内的纹理坐标,该坐标位于0.5,0.5
处。因此,要计算梯度,我们必须将距中心的距离从0-0.5到0-1进行归一化。这可以通过除以0.5或将倒数(如示例)乘以2(因为乘积始终是更好的选择)来完成比司)
简化着色器
同样,您计算每个片段的渐变颜色的方法在计算上也很昂贵。对于每个渐变(在这种情况下为3),您依次调用smoothstep
和mix
,但是对于每个片段2,这些计算都不会对计算出的颜色产生任何影响。
下面的示例通过检查距离是否在特定的渐变范围内,并且只有在该范围内然后计算分配给gl_FragColor
的颜色然后跳出循环,来减少计算量
如果您希望渐变按照第一张图片和代码(以及可接受的答案)所建议的那样变暗到边缘,或者您要在问题中使用的第二张图片,则我无法锻炼。该示例假定您要第二张图像。
const shaders = {
vs: `attribute vec2 vert;
varying vec2 uv;
void main() {
uv = (vert + 1.0) / 2.0; // normalize texture coords
gl_Position = vec4(vert,1.0);
}`,fs: `precision mediump float;
varying vec2 uv;
uniform vec3 colors[4];
uniform float steps[4];
void main(){
vec2 gradXY = abs(uv - 0.5); // 0.5 is centerX,centerY
float dist = pow(max(gradXY.x,gradXY.y) * 2.0,2.0);
float start = steps[0];
for (int i = 1; i < 4; i++) {
float end = steps[i];
if (dist >= start && dist <= end) {
gl_FragColor = vec4(mix(colors[i - 1],(dist-start) / (end-start)),1);
break;
}
start = end;
}
}`,};
const F32A = a => new Float32Array(a),UI16A = a => new Uint16Array(a);
const GLBuffer = (data,type = gl.ARRAY_BUFFER,use = gl.STATIC_DRAW,buf) => (gl.bindBuffer(type,buf = gl.createBuffer()),gl.bufferData(type,data,use),buf);
const GLLocs = (shr,type,...names) => names.reduce((o,name) => (o[name] = (gl[`get${type}Location`])(shr,name),o),{});
const GLShader = (prg,source,type = gl.FRAGMENT_SHADER,shr) => {
gl.shaderSource(shr = gl.createShader(type),source);
gl.compileShader(shr);
gl.attachShader(prg,shr);
}
var W;
const gl = canvas.getContext("webgl");
requestAnimationFrame(render);
addEventListener("resize",render);
const prog = gl.createProgram();
GLShader(prog,shaders.vs,gl.VERTEX_SHADER);
GLShader(prog,shaders.fs);
gl.linkProgram(prog);
gl.useProgram(prog);
const locs = GLLocs(prog,"Uniform","colors","steps");
const vert = GLLocs(prog,"Attrib","vert").vert;
GLBuffer(F32A([-1,1]));
GLBuffer(UI16A([1,3,3]),gl.ELEMENT_ARRAY_BUFFER);
gl.enableVertexAttribArray(vert);
gl.vertexAttribPointer(vert,0);
function render() {
gl.viewport(0,W = canvas.width = Math.min(innerWidth,innerHeight),canvas.height = W);
gl.uniform3fv(locs.colors,F32A([1,0]));
gl.uniform1fv(locs.steps,F32A([0,1/3,2/3,1]));
gl.drawElements(gl.TRIANGLES,6,0);
}
body {
margin: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
background: black;
}
<canvas id="canvas"></canvas>
问题模棱两可
您的问题中有许多歧义,以下将解决
示例行中的pow
函数...
float dist = pow(max(gradXY.x,2.0);
...是计算col时使用smoothstep(steps[i - 1],dist)
的近似值(假设dist范围为0-0.5)。如果您想要完整的Hermite曲线,可以将线替换为...
float distL = max(gradXY.x,gradXY.y) * 2.0;
float dist = distL * distL * (3.0 - 2.0 * distL);
..,如果要像问题中那样使边缘变暗,则在计算碎片颜色时,请使用第一行的下一行。注意如果使用示例代码,则假设颜色是vec4
而不是vec3
,请进行适当的修改。
FragColor = mix(colors[i - 1],(dist-start) / (end-start)) - vec4(vec3(distL * 0.5),0);
或者如果不使用Hermite曲线
FragColor = mix(colors[i - 1],(dist-start) / (end-start)) - vec4(vec3(dist * 0.5),0);