是否有广义顶点和片段着色器的概念?

Is there the notion of a generalized vertex and fragment shader?

我打算着手创建一个简单的 2D,也许是 3D,像 Pixi.js 这样的游戏系统。我注意到他们为每种类型的效果提供了着色器,还有一个通用的投影矩阵着色器,但除此之外,其他一切都发生在常规代码领域。

gl_Position = projection * model * vec4(position, 1.0);

ShaderToy 之类的东西就这样吗,玩具,看看光靠着色器你能做多少?或者真正的游戏引擎是否需要直接在着色器中实现重要功能?基本上,是否存在可用于游戏引擎中所有渲染的通用标准着色器的概念,或者您是否必须为此和那个执行一个单独的着色器?

我想知道我是否能找到游戏引擎的 keystone 着色器,one 着色器对,我需要 WebGL 中的高性能 2D 引擎,而不是 thinking/imagining 我需要根据具体情况慢慢弄清楚着色器将在游戏引擎中发挥作用。

例如,这是 Pixi.js 中的默认着色器:

attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;

varying vec2 vTextureCoord;

void main(void)
{
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
}

https://github.com/allotrop3/four/tree/master/src/shaders

简短的回答是否定的,除非您的游戏非常简单并且只需要一组适用于所有情况的固定功能,否则您无法制作一个通用着色器。

Unity 和 Unreal 等游戏引擎根据游戏开发人员使用的功能制作 thousands of shaders。即使 three.js 并不像其他引擎那么复杂,也会根据用于灯光、纹理、蒙皮、混合形状、环境映射等的每种组合的特征生成不同的着色器。

有一个“超级着色器”的概念试图做很多事情。通常这是游戏开发者用来试验的东西,因为他们知道它对于生产来说太慢了。它在现代引擎中不太常见,因为这些引擎被设计为在运行时或构建时生成着色器,因此很容易指定您想要的功能,然后引擎将生成着色器。对于没有着色器生成系统的引擎,开发人员可能会制作一个实现所有功能的着色器。一旦他们得到了他们想要的外观,他们就会将它与他们需要的功能配对 and/or 他们会添加很多条件编译宏来转换功能 on/off 然后将着色器编译成不同的版本他们需要的每个功能组合。

您可以通过查看 three.js 的着色器了解这一点。 Here is the shader generated by three.js for this program which I used this helper to view. I'd have pasted it in the question but it is 44k and S.O. only allows 30k for a message. First off it was assembled via a large number of snippets。其次,您会注意到整个代码中的各种条件复杂化指令。例子

#ifdef DITHERING
    vec3 dithering( vec3 color ) {
        float grid_position = rand( gl_FragCoord.xy );
        vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );
        dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );
        return color + dither_shift_RGB;
    }
#endif
#ifdef USE_COLOR
    varying vec3 vColor;
#endif
#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )
    varying vec2 vUv;
#endif
#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )
    varying vec2 vUv2;
#endif
#ifdef USE_MAP
    uniform sampler2D map;
#endif
#ifdef USE_ALPHAMAP
    uniform sampler2D alphaMap;
#endif
#ifdef USE_AOMAP
    uniform sampler2D aoMap;
    uniform float aoMapIntensity;
#endif
#ifdef USE_LIGHTMAP
    uniform sampler2D lightMap;
    uniform float lightMapIntensity;
#endif
#ifdef USE_EMISSIVEMAP
    uniform sampler2D emissiveMap;
#endif
#ifdef USE_ENVMAP
    uniform float envMapIntensity;
    uniform float flipEnvMap;
    uniform int maxMipLevel;
    #ifdef ENVMAP_TYPE_CUBE
        uniform samplerCube envMap;
    #else
        uniform sampler2D envMap;
    #endif
    
#endif

如果您开始启用这些功能,例如,如果您在 JavaScript 中设置 mateiral.envMap,您会看到 three.js 在着色器顶部插入 #define USE_ENVMAP沉迷于它为所有着色器片段的子集生成着色器这一事实。

这也显示了您使用现有引擎节省的工作量。 44k 的代码对于重现 three.js 给你的所有功能来说不是小数目的代码。如果您打算从头开始做事,那么至少要知道这可能是一项繁重的工作。当然,如果您制作的东西只需要一小部分功能而不需要组合,那么您只需几个 hand-written 着色器即可。

你也提到了

if I can just find that keystone shader for the game engine, the one shader pair I need for a high-performance 2D engine in WebGL

high-performance 2D 引擎可以说没有梯形校正着色器这样的东西。如果你想要性能,你需要每个着色器做尽可能少的事情,这与梯形失真着色器相反。

也就是说,这取决于 2D 游戏。如果您想制作愤怒的小鸟,您在问题中发布的顶点着色器可能是您可能需要的唯一着色器。愤怒的小鸟没有特殊效果。它只是绘制简单的纹理四边形。所以只是

attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;

varying vec2 vTextureCoord;

void main(void)
{
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
}

和像

这样的片段着色器
precision mediump float;

varying vec2 vTextureCoord;
uniform sampler2D texture;
uniform sampler2D colorMult;

void main()
{
    gl_FragColor = texture2D(texture, vTextureCoord) * colorMult;
}

对于 2010 年之前制作的几乎所有 2D 游戏来说已经足够了。从那以后的 2D 游戏(我随便选了一个日期)经常使用自定义着色器来实现特殊效果或进行优化。例如,某些类型的粒子效果很容易使用自定义着色器制作。 this game is made with this shader. If you skip to 00:50 you'll see 3 example. The 2 portals under the cake, the candles on the cake, the fireworks... also if you look close in parts of the video you can see particles where characters land on the ground after jumping, all the same stateless particle shader since running particles in Javascript and individually uploading their state would arguably be slow. Another example is the backgrounds are drawn with a tiling shader like 中的每个粒子效果。在我看来,这比使用上面的着色器并为图块生成顶点网格更容易。

Shadertoy 大部分是玩具。参见 this