我试图像设计应用程序(例如Photoshop)那样尝试制作渐变色,但却无法获得我想要的确切结果。
我的着色器创造了非常漂亮的渐变效果'但也包含与我想要切换的颜色不同的其他颜色。
看起来不错,但我的目标是稍后添加混合功能并制作一种色彩校正着色器。但首先我必须得到正确的颜色。
这是我的片段着色器 http://player.thebookofshaders.com/?log=171119111216
uniform vec2 u_resolution;
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color1 = vec3(1.9,0.55,0);
vec3 color2 = vec3(0.226,0.000,0.615);
float mixValue = distance(st,vec2(0,1));
vec3 color = mix(color1,color2,mixValue);
gl_FragColor = vec4(color,mixValue);
}
提前致谢..
答案 0 :(得分:5)
在回答问题的标题时,您可能还想考虑在其他颜色空间中进行混音。您的代码在RGB空间中混合,但您会在不同的空间中获得不同的结果。
实施例
const gl = document.createElement("canvas").getContext("webgl");
gl.canvas.width = 100;
gl.canvas.height = 100;
gl.viewport(0, 0, 100, 100);
const vsrc = `
void main() {
gl_PointSize = 100.0;
gl_Position = vec4(0, 0, 0, 1);
}
`;
const fRGB = `
precision mediump float;
uniform vec3 color1;
uniform vec3 color2;
void main() {
vec2 st = gl_PointCoord;
float mixValue = distance(st, vec2(0, 1));
vec3 color = mix(color1, color2, mixValue);
gl_FragColor = vec4(color, 1);
}
`;
const fHSV = `
precision mediump float;
uniform vec3 color1;
uniform vec3 color2;
// from: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c) {
c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
vec2 st = gl_PointCoord;
float mixValue = distance(st, vec2(0, 1));
vec3 hsv1 = rgb2hsv(color1);
vec3 hsv2 = rgb2hsv(color2);
// mix hue in toward closest direction
float hue = (mod(mod((hsv2.x - hsv1.x), 1.) + 1.5, 1.) - 0.5) * mixValue + hsv1.x;
vec3 hsv = vec3(hue, mix(hsv1.yz, hsv2.yz, mixValue));
vec3 color = hsv2rgb(hsv);
gl_FragColor = vec4(color, 1);
}
`;
const fHSL = `
precision mediump float;
uniform vec3 color1;
uniform vec3 color2;
const float Epsilon = 1e-10;
vec3 rgb2hcv(in vec3 RGB)
{
// Based on work by Sam Hocevar and Emil Persson
vec4 P = lerp(vec4(RGB.bg, -1.0, 2.0/3.0), vec4(RGB.gb, 0.0, -1.0/3.0), step(RGB.b, RGB.g));
vec4 Q = mix(vec4(P.xyw, RGB.r), vec4(RGB.r, P.yzx), step(P.x, RGB.r));
float C = Q.x - min(Q.w, Q.y);
float H = abs((Q.w - Q.y) / (6. * C + Epsilon) + Q.z);
return vec3(H, C, Q.x);
}
vec3 rgb2hsl(in vec3 RGB)
{
vec3 HCV = rgb2hcv(RGB);
float L = HCV.z - HCV.y * 0.5;
float S = HCV.y / (1 - abs(L * 2. - 1.) + Epsilon);
return vec3(HCV.x, S, L);
}
vec3 hsl2rgb(vec3 c)
{
c = vec3(fract(c.x), clamp(c.yz, 0.0, 1.0));
vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
}
void main() {
vec2 st = gl_PointCoord;
float mixValue = distance(st, vec2(0, 1));
vec3 hsl1 = rgb2hsl(color1);
vec3 hsl2 = rgb2hsl(color2);
// mix hue in toward closest direction
float hue = (mod(mod((hsl2.x - hsl1.x), 1.) + 1.5, 1.) - 0.5) * mixValue + hsl1.x;
vec3 hsl = vec3(hue, mix(hsl1.yz, hsl2.yz, mixValue));
vec3 color = hsl2rgb(hsv);
gl_FragColor = vec4(color, 1);
}
`;
const fLAB = `
precision mediump float;
uniform vec3 color1;
uniform vec3 color2;
// from: https://code.google.com/archive/p/flowabs/source
vec3 rgb2xyz( vec3 c ) {
vec3 tmp;
tmp.x = ( c.r > 0.04045 ) ? pow( ( c.r + 0.055 ) / 1.055, 2.4 ) : c.r / 12.92;
tmp.y = ( c.g > 0.04045 ) ? pow( ( c.g + 0.055 ) / 1.055, 2.4 ) : c.g / 12.92,
tmp.z = ( c.b > 0.04045 ) ? pow( ( c.b + 0.055 ) / 1.055, 2.4 ) : c.b / 12.92;
return 100.0 * tmp *
mat3( 0.4124, 0.3576, 0.1805,
0.2126, 0.7152, 0.0722,
0.0193, 0.1192, 0.9505 );
}
vec3 xyz2lab( vec3 c ) {
vec3 n = c / vec3( 95.047, 100, 108.883 );
vec3 v;
v.x = ( n.x > 0.008856 ) ? pow( n.x, 1.0 / 3.0 ) : ( 7.787 * n.x ) + ( 16.0 / 116.0 );
v.y = ( n.y > 0.008856 ) ? pow( n.y, 1.0 / 3.0 ) : ( 7.787 * n.y ) + ( 16.0 / 116.0 );
v.z = ( n.z > 0.008856 ) ? pow( n.z, 1.0 / 3.0 ) : ( 7.787 * n.z ) + ( 16.0 / 116.0 );
return vec3(( 116.0 * v.y ) - 16.0, 500.0 * ( v.x - v.y ), 200.0 * ( v.y - v.z ));
}
vec3 rgb2lab(vec3 c) {
vec3 lab = xyz2lab( rgb2xyz( c ) );
return vec3( lab.x / 100.0, 0.5 + 0.5 * ( lab.y / 127.0 ), 0.5 + 0.5 * ( lab.z / 127.0 ));
}
vec3 lab2xyz( vec3 c ) {
float fy = ( c.x + 16.0 ) / 116.0;
float fx = c.y / 500.0 + fy;
float fz = fy - c.z / 200.0;
return vec3(
95.047 * (( fx > 0.206897 ) ? fx * fx * fx : ( fx - 16.0 / 116.0 ) / 7.787),
100.000 * (( fy > 0.206897 ) ? fy * fy * fy : ( fy - 16.0 / 116.0 ) / 7.787),
108.883 * (( fz > 0.206897 ) ? fz * fz * fz : ( fz - 16.0 / 116.0 ) / 7.787)
);
}
vec3 xyz2rgb( vec3 c ) {
vec3 v = c / 100.0 * mat3(
3.2406, -1.5372, -0.4986,
-0.9689, 1.8758, 0.0415,
0.0557, -0.2040, 1.0570
);
vec3 r;
r.x = ( v.r > 0.0031308 ) ? (( 1.055 * pow( v.r, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.r;
r.y = ( v.g > 0.0031308 ) ? (( 1.055 * pow( v.g, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.g;
r.z = ( v.b > 0.0031308 ) ? (( 1.055 * pow( v.b, ( 1.0 / 2.4 ))) - 0.055 ) : 12.92 * v.b;
return r;
}
vec3 lab2rgb(vec3 c) {
return xyz2rgb( lab2xyz( vec3(100.0 * c.x, 2.0 * 127.0 * (c.y - 0.5), 2.0 * 127.0 * (c.z - 0.5)) ) );
}
void main() {
vec2 st = gl_PointCoord;
float mixValue = distance(st, vec2(0, 1));
vec3 lab1 = rgb2lab(color1);
vec3 lab2 = rgb2lab(color2);
vec3 lab = mix(lab1, lab2, mixValue);
vec3 color = lab2rgb(lab);
gl_FragColor = vec4(color, 1);
}
`;
function draw(gl, shaders, color1, color2, label) {
const programInfo = twgl.createProgramInfo(gl, shaders);
gl.useProgram(programInfo.program);
twgl.setUniforms(programInfo, {
color1: color1,
color2: color2,
});
gl.drawArrays(gl.POINTS, 0, 1);
const div = document.createElement("div");
const img = new Image();
img.src = gl.canvas.toDataURL();
div.appendChild(img);
const inner = document.createElement("span");
inner.textContent = label;
div.appendChild(inner);
document.body.appendChild(div);
}
const color1 = [1.0, 0.55, 0];
const color2 = [0.226, 0.000, 0.615];
draw(gl, [vsrc, fRGB], color1, color2, "rgb");
draw(gl, [vsrc, fHSV], color1, color2, "hsv");
draw(gl, [vsrc, fHSV], color1, color2, "hsl");
draw(gl, [vsrc, fLAB], color1, color2, "lab");

img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
&#13;
我还建议在渐变纹理中传递颜色。 Nx1纹理并使用
color = texture2D(
rampTexture,
vec2((mixValue * (rampWidth - 1.) + .5) / rampWidth, 0.5)).rgb;
然后您可以轻松地混合2种颜色,3种颜色,20种颜色。您也可以通过重复颜色等来区分颜色。
示例:
const gl = document.createElement("canvas").getContext("webgl");
gl.canvas.width = 100;
gl.canvas.height = 100;
gl.viewport(0, 0, 100, 100);
const vsrc = `
void main() {
gl_PointSize = 100.0;
gl_Position = vec4(0, 0, 0, 1);
}
`;
const fsrc = `
precision mediump float;
uniform sampler2D rampTexture;
uniform float rampWidth;
void main() {
vec2 st = gl_PointCoord;
float mixValue = distance(st, vec2(0, 1));
vec3 color = texture2D(
rampTexture,
vec2((mixValue * (rampWidth - 1.) + .5) / rampWidth, 0.5)).rgb;
gl_FragColor = vec4(color, 1);
}
`;
const programInfo = twgl.createProgramInfo(gl, [vsrc, fsrc]);
gl.useProgram(programInfo.program);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
function draw(gl, ramp, label) {
const width = ramp.length;
gl.bindTexture(gl.TEXTURE_2D, tex);
const level = 0;
const internalFormat = gl.RGB;
const height = 1;
const border = 0;
const format = gl.RGB;
const type = gl.UNSIGNED_BYTE;
const rampData = new Uint8Array([].concat(...ramp).map(v => v * 255));
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
format, type, rampData);
twgl.setUniforms(programInfo, {
rampTexture: tex,
rampWidth: width,
});
gl.drawArrays(gl.POINTS, 0, 1);
const div = document.createElement("div");
const img = new Image();
img.src = gl.canvas.toDataURL();
div.appendChild(img);
const inner = document.createElement("span");
inner.textContent = label;
div.appendChild(inner);
document.body.appendChild(div);
}
const color1 = [1.0, 0.55, 0];
const color2 = [0.226, 0.000, 0.615];
const r = [1, 0, 0];
const g = [0, 1, 0];
const b = [0, 0, 1];
const w = [1, 1, 1];
draw(gl, [color1, color2], "color1->color2");
draw(gl, [r, g], "red->green");
draw(gl, [r, g, b], "r->g->b");
draw(gl, [r, b, r, b, r], "r->b->r->b->r");
draw(gl, [g, b, b, b, g], "g->b->b->b->g");
&#13;
img { border: 1px solid black; margin: 2px; }
span { display: block; }
div { display: inline-block; text-align: center; }
&#13;
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
&#13;
答案 1 :(得分:3)
设置颜色值时,Ther是一个简单的错误。当您设置橙色时,使用1.9作为红色通道的值而不是1.0。
将您的代码更改为:
vec3 color1 = vec3(1.0, 0.55, 0.0); // 1.0 insted of 1.9
请注意,最终的颜色通道被固定为[0,1],但由于您使用mix
来插值颜色,因此大于1.0的颜色通道会提升渐变中颜色的部分。
预览:
var ShaderProgram = {};
ShaderProgram.Create = function( shaderList ) {
var shaderObjs = [];
for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
if ( shderObj == 0 )
return 0;
shaderObjs.push( shderObj );
}
var progObj = this.LinkProgram( shaderObjs )
if ( progObj != 0 ) {
progObj.attribIndex = {};
var noOfAttributes = gl.getProgramParameter( progObj, gl.ACTIVE_ATTRIBUTES );
for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
var name = gl.getActiveAttrib( progObj, i_n ).name;
progObj.attribIndex[name] = gl.getAttribLocation( progObj, name );
}
progObj.unifomLocation = {};
var noOfUniforms = gl.getProgramParameter( progObj, gl.ACTIVE_UNIFORMS );
for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
var name = gl.getActiveUniform( progObj, i_n ).name;
progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
}
}
return progObj;
}
ShaderProgram.AttributeIndex = function( progObj, name ) { return progObj.attribIndex[name]; }
ShaderProgram.UniformLocation = function( progObj, name ) { return progObj.unifomLocation[name]; }
ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); }
ShaderProgram.SetUniformF2 = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.CompileShader = function( source, shaderStage ) {
var shaderScript = document.getElementById(source);
if (shaderScript) {
source = "";
var node = shaderScript.firstChild;
while (node) {
if (node.nodeType == 3) source += node.textContent;
node = node.nextSibling;
}
}
var shaderObj = gl.createShader( shaderStage );
gl.shaderSource( shaderObj, source );
gl.compileShader( shaderObj );
var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
return status ? shaderObj : 0;
}
ShaderProgram.LinkProgram = function( shaderObjs ) {
var prog = gl.createProgram();
for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
gl.attachShader( prog, shaderObjs[i_sh] );
gl.linkProgram( prog );
status = gl.getProgramParameter( prog, gl.LINK_STATUS );
if ( !status ) alert("Could not initialise shaders");
gl.useProgram( null );
return status ? prog : 0;
}
function drawScene(){
var canvas = document.getElementById( "ogl-canvas" );
var vp = [canvas.width, canvas.height];
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.enable( gl.DEPTH_TEST );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformF2( progDraw, "u_resolution", vp )
gl.enableVertexAttribArray( progDraw.inPos );
gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
gl.disableVertexAttribArray( progDraw.pos );
}
var gl;
var prog;
var bufObj = {};
function sceneStart() {
var canvas = document.getElementById( "ogl-canvas");
gl = canvas.getContext( "experimental-webgl", { premultipliedAlpha: true } );
if ( !gl )
return;
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
if ( prog == 0 )
return;
var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
var inx = [ 0, 1, 2, 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 );
setInterval(drawScene, 50);
}
&#13;
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
vertPos = inPos;
gl_Position = vec4( inPos.xy, 0.0, 1.0 );
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform vec2 u_resolution;
void main()
{
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color1 = vec3(1.0,0.55,0);
vec3 color2 = vec3(0.226,0.000,0.615);
float mixValue = distance(st,vec2(0,1));
vec3 color = mix(color1,color2,mixValue);
gl_FragColor = vec4(color,1.0);
}
</script>
<body onload="sceneStart();">
<canvas id="ogl-canvas" style="border: none;" width="256" height="256"></canvas>
</body>
&#13;