我一直在尝试使用Shadertoy的鱼眼着色器。
我添加了自己的帧分辨率,并更改了一些关键字(texture
-> texture2D
,fragColor
-> gl_FragColor
),仅此而已。
我真的不知道为什么它不起作用以及如何调试它。
结果,我得到了单色灰色图像。
这是我的片段着色器的代码:
precision mediump float;
uniform vec4 v_Color;
uniform sampler2D u_Texture;
varying vec2 v_TexCoordinate;
#define RESOLUTION_WIDTH 375.0
#define RESOLUTION_HEIGHT 211.0
#define POWER 2.0
void main() {
vec2 fragCoord = v_TexCoordinate;
vec2 iResolution = vec2(RESOLUTION_WIDTH, RESOLUTION_HEIGHT);
vec2 p = fragCoord.xy / iResolution.x; // normalized coords with some cheat
float prop = iResolution.x / iResolution.y;
vec2 m = vec2(0.5, 0.5 / prop); // center coords
vec2 d = p - m; // vector from center to current fragment
float r = sqrt(dot(d, d)); // distance of pixel from center
float power = POWER;
float bind; // radius of 1:1 effect
if (power > 0.0)
bind = sqrt(dot(m, m)); // stick to corners
else {
if (prop < 1.0)
bind = m.x;
else
bind = m.y;
} // stick to borders
// Weird formulas
vec2 uv;
if (power > 0.0) // fisheye
uv = m + normalize(d) * tan(r * power) * bind / tan( bind * power);
else if (power < 0.0) // antifisheye
uv = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
else uv = p; // no effect for power = 1.0
vec3 col = texture2D(u_Texture, vec2(uv.x, -uv.y * prop)).xyz; // Second part of cheat
gl_FragColor = vec4(col, 1.0);
}
这是我原来的着色器,用于显示效果理想的图像:
precision mediump float;
uniform vec4 v_Color;
uniform sampler2D u_Texture;
varying vec2 v_TexCoordinate;
void main() {
// premultiplied alpha
vec4 texColor = texture2D(u_Texture, v_TexCoordinate);
// Scale the texture RGB by the vertex color
texColor.rgb *= v_Color.rgb;
// Scale the texture RGBA by the vertex alpha to reinstate premultiplication
gl_FragColor = texColor * v_Color.a;
}
以下是ShaderToy上预期结果的链接:
原始结果图片:
使用我的着色器:
使用Rabbid76解决方案:
功率= 1.1时:
使用解决方案n2且功效= 10(更大的图片可以更好地看到):
文字后面有一些背景,不要关注它;)
答案 0 :(得分:1)
在着色器代码fragCoord
中假定为窗口坐标,最小值为(0,0),最大值为视口的宽度和高度。但是在您的代码中,v_TexCoordinate
被分配给fragCoord
。 v_TexCoordinate
是[0,1]范围内的纹理Corodiante。
使用gl_FragCoord
代替v_TexCoordinate
:
// vec2 fragCoord = v_TexCoordinate; <--- delete
vec2 fragCoord = gl_FragCoord.xy;
vec2 p = fragCoord.xy / iResolution.x;
或跳过除以窗口分辨率:
vec2 fragCoord = v_TexCoordinate;
// vec2 p = fragCoord.xy / iResolution.x; <-- delete
vec2 p = fragCoord.xy * vec2(1.0, iResolution.y/iResolution.x);
如果不需要进行宽高比校正,那么甚至可以完成:
vec2 p = v_TexCoordinate.xy;
请参阅WebGL示例,在该示例中,我使用您的原始着色器代码并应用了建议的更改:
(function loadscene() {
var gl, canvas, prog, bufObj = {};
var texture;
function render(deltaMS) {
texture.bound = texture.bound || texture.bind( 0 );
gl.viewport( 0, 0, vp_size[0], vp_size[1] );
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 );
ShProg.Use( progDraw );
ShProg.SetF2( progDraw, "resolution", vp_size );
ShProg.SetI1( progDraw, "u_texture", 0 );
VertexBuffer.Draw( bufRect );
requestAnimationFrame(render);
}
function initScene() {
canvas = document.getElementById( "texture-canvas");
gl = canvas.getContext( "experimental-webgl" );
//gl = canvas.getContext( "webgl2" );
if ( !gl )
return;
progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progDraw.inPos = gl.getAttribLocation( progDraw.progObj, "inPos" );
if ( progDraw.progObj == 0 )
return;
bufRect = VertexBuffer.Create(
[ { data : [ -1, -1, 1, -1, 1, 1, -1, 1 ], attrSize : 2, attrLoc : progDraw.inPos } ],
[ 0, 1, 2, 0, 2, 3 ] );
texture = new Texture( "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/supermario.jpg" );
texture.bound = false;
window.onresize = resize;
resize();
requestAnimationFrame(render);
}
function resize() {
//vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
vp_size = [window.innerWidth, window.innerHeight]
vp_size[0] = vp_size[1] = Math.min(vp_size[0], vp_size[1]);
//vp_size = [256, 256]
canvas.width = vp_size[0];
canvas.height = vp_size[1];
}
var ShProg = {
Create: function (shaderList) {
var shaderObjs = [];
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
var noOfAttributes = gl.getProgramParameter(prog.progObj, gl.ACTIVE_ATTRIBUTES);
for (var i_n = 0; i_n < noOfAttributes; ++i_n) {
var name = gl.getActiveAttrib(prog.progObj, i_n).name;
prog.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
var noOfUniforms = gl.getProgramParameter(prog.progObj, gl.ACTIVE_UNIFORMS);
for (var i_n = 0; i_n < noOfUniforms; ++i_n) {
var name = gl.getActiveUniform(prog.progObj, i_n).name;
prog.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: function (source, shaderStage) {
var shaderScript = document.getElementById(source);
if (shaderScript)
source = shaderScript.text;
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 : null;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };
var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: [], attr: [], inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
Draw: function(bufObj) {
for (var i=0; i<bufObj.buf.length; ++i) {
gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.buf[i]);
gl.vertexAttribPointer(bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray( bufObj.attr[i].loc);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };
class Texture {
constructor( name, dflt ) {
let texture = this;
this.dflt = dflt || [128,128,128,255]
let image = { "cx": this.dflt.w || 1, "cy": this.dflt.h || 1, "plane": this.dflt.p || this.dflt };
this.size = [image.cx, image.cy];
this.dummyObj = Texture.createTexture2D( image, true )
this.image = new Image(64,64);
this.image.setAttribute('crossorigin', 'anonymous');
this.image.onload = function () {
let cx = 1 << 31 - Math.clz32(texture.image.naturalWidth);
if ( cx < texture.image.naturalWidth ) cx *= 2;
let cy = 1 << 31 - Math.clz32(texture.image.naturalHeight);
if ( cy < texture.image.naturalHeight ) cy *= 2;
var canvas = document.createElement( 'canvas' );
canvas.width = cx;
canvas.height = cy;
var context = canvas.getContext( '2d' );
context.drawImage( texture.image, 0, 0, canvas.width, canvas.height );
texture.textureObj = Texture.createTexture2D( canvas, true );
texture.size = [cx, cy];
}
this.image.src = name;
}
static createTexture2D( image, flipY ) {
let t = gl.createTexture();
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture( gl.TEXTURE_2D, t );
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, flipY != undefined && flipY == true );
if ( image.cx && image.cy && image.plane )
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, image.cx, image.cy, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(image.plane) );
else
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
gl.bindTexture( gl.TEXTURE_2D, null );
return t;
}
bind( texUnit = 0 ) {
gl.activeTexture( gl.TEXTURE0 + texUnit );
if ( this.textureObj ) {
gl.bindTexture( gl.TEXTURE_2D, this.textureObj );
return true;
}
gl.bindTexture( gl.TEXTURE_2D, this.dummyObj );
return false;
}
};
initScene();
})();
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
void main()
{
gl_Position = vec4( inPos.xy, 0.0, 1.0 );
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
uniform vec2 resolution;
uniform sampler2D u_Texture;
#define RESOLUTION_WIDTH 375.0
#define RESOLUTION_HEIGHT 211.0
#define POWER 2.0
void main( void )
{
vec2 fragCoord = gl_FragCoord.xy;
vec2 iResolution = resolution;
//vec2 fragCoord = v_TexCoordinate;
//vec2 iResolution = vec2(RESOLUTION_WIDTH, RESOLUTION_HEIGHT);
vec2 p = fragCoord.xy / iResolution.x; // normalized coords with some cheat
float prop = iResolution.x / iResolution.y;
vec2 m = vec2(0.5, 0.5 / prop); // center coords
vec2 d = p - m; // vector from center to current fragment
float r = sqrt(dot(d, d)); // distance of pixel from center
float power = POWER;
float bind; // radius of 1:1 effect
if (power > 0.0)
bind = sqrt(dot(m, m)); // stick to corners
else {
if (prop < 1.0)
bind = m.x;
else
bind = m.y;
} // stick to borders
// Weird formulas
vec2 uv;
if (power > 0.0) // fisheye
uv = m + normalize(d) * tan(r * power) * bind / tan( bind * power);
else if (power < 0.0) // antifisheye
uv = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
else uv = p; // no effect for power = 1.0
vec3 col = texture2D(u_Texture, vec2(uv.x, -uv.y * prop)).xyz; // Second part of cheat
gl_FragColor = vec4(col, 1.0);
}
</script>
<body>
<canvas id="texture-canvas" style="border: none"></canvas>
</body>