OpenGL代码欣赏——在fragShader中实现“3D纹理”,实现透视效果和光栅化
·
一、 序言
最近逛shadertoy看到有人把透视转换、光照、3D转2D光栅化转移到了fragShader实现,虽然感觉没啥实用性,过于麻烦,但觉得非常厉害又有趣,让我感觉看到了新鲜事物一样。拜读之后对他的代码有了一点个人理解,写一个笔记记录一下。
二、 立方体的建立
这是整个shader代码中最简单的部分:
2.1、声明立方体坐标:

对应的实际位置假设:

2.2、 三角面的建立
立方体的每个面都需要两个三角形构成,每个三角形需要三个顶点,每个顶点需要一个三维向量表达。同时,由于每个面需要都需要进行贴图,因此需要三个纹理采样二维坐标对应面中的3个采样位置。然后因为需要实现光照,还需要添加一个3维法向量。
具体数据结构如下:

2.3、 立方体三角形数组赋值,没啥好解释的:

三、 投影变换、光栅化:
再来看看mainImage的主过程,这部分才是最复杂的部分:
3.1、计算世界坐标:
这一步是把VertexShader或者应用层本身该干的活挪过来了哈哈:
3.2、 坐标重整:
由于fragshader传入的fragCoord坐标是左上角(0,0),右下角(1,1)的,因此要模拟OpenGL本身0,0在中心、最小值为-1、最大值为1的坐标系,必须先把值乘以2,再减去最大的x、y值,除以iResolution.y像素宽度量化为统一的单位区间值。

3.3、 采样和栅格化
过程概括就是,遍历每个三角形面,把三角形面经过投影变换后的坐标转换为对纹理的采样坐标,从而在三角形面的相对位置中,把纹理映射到三角面中,并根据法向量和光源位置的点积值决定最后的色深,最后输出到当前正在处理的片源中。

实际上,如果只是想使用fragshader实现一个旋转的立方体,代码还可以精简一下:
// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// a perspective correct triangle rasterizer, in a shader!! :D
mat4 setRotation( float x, float y, float z )
{
float a = sin(x); float b = cos(x);
float c = sin(y); float d = cos(y);
float e = sin(z); float f = cos(z);
float ac = a*c;
float bc = b*c;
return mat4( d*f, d*e, -c, 0.0,
ac*f-b*e, ac*e+b*f, a*d, 0.0,
bc*f+a*e, bc*e-a*f, b*d, 0.0,
0.0, 0.0, 0.0, 1.0 );
}
mat4 setTranslation( float x, float y, float z )
{
return mat4( 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
x, y, z, 1.0 );
}
struct Triangle
{
vec3 a; vec2 aUV;
vec3 b; vec2 bUV;
vec3 c; vec2 cUV;
vec3 n;
};
const vec3 vertsCube[8] = vec3[8]( vec3( -1.0, -1.0, -1.0 ),
vec3( -1.0, -1.0, 1.0 ),
vec3( -1.0, 1.0, -1.0 ),
vec3( -1.0, 1.0, 1.0 ),
vec3( 1.0, -1.0, -1.0 ),
vec3( 1.0, -1.0, 1.0 ),
vec3( 1.0, 1.0, -1.0 ),
vec3( 1.0, 1.0, 1.0 ) );
Triangle triangles[12];
void createCube( void )
{
triangles[0].a = vertsCube[1]; triangles[0].aUV = vec2(0.0,0.0);
triangles[0].b = vertsCube[5]; triangles[0].bUV = vec2(1.0,0.0);
triangles[0].c = vertsCube[7]; triangles[0].cUV = vec2(1.0,1.0);
triangles[0].n = vec3( 0.0, 0.0, 1.0 );
triangles[1].a = vertsCube[1]; triangles[1].aUV = vec2(0.0,0.0),
triangles[1].b = vertsCube[7]; triangles[1].bUV = vec2(1.0,1.0),
triangles[1].c = vertsCube[3]; triangles[1].cUV = vec2(0.0,1.0),
triangles[1].n = vec3( 0.0, 0.0, 1.0 );
triangles[2].a = vertsCube[5]; triangles[2].aUV = vec2(0.0,0.0);
triangles[2].b = vertsCube[4]; triangles[2].bUV = vec2(1.0,0.0);
triangles[2].c = vertsCube[6]; triangles[2].cUV = vec2(1.0,1.0);
triangles[2].n = vec3( 1.0, 0.0, 0.0 );
triangles[3].a = vertsCube[5]; triangles[3].aUV = vec2(0.0,0.0);
triangles[3].b = vertsCube[6]; triangles[3].bUV = vec2(1.0,1.0);
triangles[3].c = vertsCube[7]; triangles[3].cUV = vec2(0.0,1.0);
triangles[3].n = vec3( 1.0, 0.0, 0.0 );
triangles[4].a = vertsCube[3]; triangles[4].aUV = vec2(0.0,0.0);
triangles[4].b = vertsCube[7]; triangles[4].bUV = vec2(1.0,0.0);
triangles[4].c = vertsCube[6];;triangles[4].cUV = vec2(1.0,1.0);
triangles[4].n = vec3( 0.0, 1.0, 0.0 );
triangles[5].a = vertsCube[3]; triangles[5].aUV = vec2(0.0,0.0);
triangles[5].b = vertsCube[6]; triangles[5].bUV = vec2(1.0,1.0);
triangles[5].c = vertsCube[2]; triangles[5].cUV = vec2(0.0,1.0);
triangles[5].n = vec3( 0.0, 1.0, 0.0 );
triangles[6].a = vertsCube[0]; triangles[6].aUV = vec2(1.0,0.0);
triangles[6].b = vertsCube[6]; triangles[6].bUV = vec2(0.0,1.0);
triangles[6].c = vertsCube[4]; triangles[6].cUV = vec2(0.0,0.0);
triangles[6].n = vec3( 0.0, 0.0, -1.0 );
triangles[7].a = vertsCube[0]; triangles[7].aUV = vec2(1.0,0.0);
triangles[7].b = vertsCube[2]; triangles[7].bUV = vec2(1.0,1.0);
triangles[7].c = vertsCube[6]; triangles[7].cUV = vec2(0.0,1.0);
triangles[7].n = vec3( 0.0, 0.0, -1.0 );
triangles[8].a = vertsCube[1]; triangles[8].aUV = vec2(1.0,0.0);
triangles[8].b = vertsCube[2]; triangles[8].bUV = vec2(0.0,1.0);
triangles[8].c = vertsCube[0]; triangles[8].cUV = vec2(0.0,0.0);
triangles[8].n = vec3( -1.0, 0.0, 0.0 );
triangles[9].a = vertsCube[1]; triangles[9].aUV = vec2(1.0,0.0);
triangles[9].b = vertsCube[3]; triangles[9].bUV = vec2(1.0,1.0);
triangles[9].c = vertsCube[2]; triangles[9].cUV = vec2(0.0,1.0);
triangles[9].n = vec3( -1.0, 0.0, 0.0 );
triangles[10].a = vertsCube[1]; triangles[10].aUV = vec2(0.0,0.0);
triangles[10].b = vertsCube[0]; triangles[10].bUV = vec2(0.0,1.0);
triangles[10].c = vertsCube[4]; triangles[10].cUV = vec2(1.0,1.0);
triangles[10].n = vec3( 0.0, -1.0, 0.0 );
triangles[11].a = vertsCube[1]; triangles[11].aUV = vec2(0.0,0.0);
triangles[11].b = vertsCube[4]; triangles[11].bUV = vec2(1.0,1.0);
triangles[11].c = vertsCube[5]; triangles[11].cUV = vec2(1.0,0.0);
triangles[11].n = vec3( 0.0, -1.0, 0.0 );
}
float cross2d( in vec2 a, in vec2 b )
{
return a.x*b.y - a.y*b.x;
}
vec3 pixelShader(in vec2 uv, in vec3 wnor )
{
vec3 mate = texture( iChannel0, uv ).xyz * abs(wnor.x) +
texture( iChannel1, uv ).xyz * abs(wnor.y) +
texture( iChannel2, uv ).xyz * abs(wnor.z);
return mate;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
mat4 mdv = setTranslation( 0.0, 0.5, -5.0 ) *
setRotation( 0.6, 0.0, 0.0 ) *
setRotation( 0.0, 3.1*(0.3*iTime), 0.0 );
vec2 px = (2.0*fragCoord-iResolution.xy) / iResolution.y;
createCube();
vec3 color = vec3( 0.0, 0.0, 0.0 );
// render triangles
for( int i=0; i<12; i++ )
{
// transform to eye space
vec3 ep0 = (mdv * vec4(triangles[i].a,1.0)).xyz;
vec3 ep1 = (mdv * vec4(triangles[i].b,1.0)).xyz;
vec3 ep2 = (mdv * vec4(triangles[i].c,1.0)).xyz;
vec3 nor = (mdv * vec4(triangles[i].n,0.0)).xyz;
// transform to clip space
float w0 = 1.0/ep0.z;
float w1 = 1.0/ep1.z;
float w2 = 1.0/ep2.z;
vec2 cp0 = 2.0*ep0.xy * -w0;
vec2 cp1 = 2.0*ep1.xy * -w1;
vec2 cp2 = 2.0*ep2.xy * -w2;
// fetch vertex attributes, and divide by z
vec2 u0 = triangles[i].aUV * w0;
vec2 u1 = triangles[i].bUV * w1;
vec2 u2 = triangles[i].cUV * w2;
//-----------------------------------
// rasterize
//-----------------------------------
// calculate areas for subtriangles
vec3 di = vec3( cross2d( cp1 - cp0, px - cp0 ),
cross2d( cp2 - cp1, px - cp1 ),
cross2d( cp0 - cp2, px - cp2 ) );
// if all positive, point is inside triangle
if( all(greaterThan(di,vec3(0.0))) )
{
// calc barycentric coordinates
vec3 ba = di.yzx / (di.x+di.y+di.z);
// barycentric interpolation of attributes and 1/z
float iz = ba.x*w0 + ba.y*w1 + ba.z*w2;
vec2 uv = ba.x*u0 + ba.y*u1 + ba.z*u2;
// recover interpolated attributes
float z = 1.0/iz;
uv *= z;
color = pixelShader(uv, triangles[i].n);
}
}
fragColor = vec4(color,1.0);
}
效果:

这个作者的算法,看起来可以在任何充分可并行的结构中进行绘图,还是挺有意思的。这个解读仅仅是业余时间的消遣,如果有理解错误的地方请指出哈。
更多推荐


所有评论(0)