ThreeJS 预定义着色器属性/统一变量

29

在使用过无添加库和GLSL着色器的“常规”WebGL之后,我开始尝试使用ThreeJS的WebGL渲染器。现在我正在尝试在我的ThreeJS程序中编写自定义着色器,并注意到ThreeJS负责很多标准内容,例如投影矩阵和模型/视图矩阵。我的简单顶点着色器现在看起来像这样:

// All of these seem to be predefined:
// vec3 position;
// mat4 projectionMatrix;
// mat4 modelViewMatrix;
// mat3 normalMatrix;
// vec3 normal;

// I added this
varying vec3 vNormal;

void main() {
    vNormal = normalMatrix * vec3(normal);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
我的问题是:哪些其他变量(我假设它们是统一的)在顶点和片段着色器中是预定义的,我可以使用?ThreeJS是否可以帮助处理光向量/光颜色,例如(当然假设我已将一个或多个光源添加到我的ThreeJS场景中)?
更新(2014年10月9日):这个问题已经被浏览了很多次,用户Killah提到现有的答案在当前版本的three.js中不再导致解决方案。我添加并接受了自己的答案,请参见下文。
4个回答

28

对于制服,简短的答案如下:

在顶点着色器中

"uniform mat4 modelMatrix;",
"uniform mat4 modelViewMatrix;",
"uniform mat4 projectionMatrix;",
"uniform mat4 viewMatrix;",
"uniform mat3 normalMatrix;",
"uniform vec3 cameraPosition;",

并且在片元着色器中

"uniform mat4 viewMatrix;",
"uniform vec3 cameraPosition;",

对于完整的答案,涉及到制服和属性,您的自定义着色器具有字符串变量prefixVertexprefixFragment预先添加。

var vertexGlsl = prefixVertex + vertexShader;
var fragmentGlsl = prefixFragment + fragmentShader;

var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

WebGLProgram.js或非压缩版本的three.js中可以找到prefixVertexprefixFragment变量的定义。

编辑:已更新至three.js r.73


非常感谢您的回答,但是最后一部分让我有些困惑。这些变量定义的作用是什么?您列出了 var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); 但我不确定我们何时需要使用它们。 - Startec

11
您可以在着色器中使用的uniform取决于材质的设置:您启用了光照吗?顶点颜色?蒙皮?...... Three JS创建的程序非常依赖一些定义(代码中的#ifdef),这些定义根据我上面提到的参数注入到程序顶部。我发现了解正在发生的事情的最佳方法是打印Three JS生成的着色器:由于您已经了解GLSL,您将轻松理解代码的含义以及可以使用哪些uniform。在Three JS源代码中查找buildProgram,然后(r57):
var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader );
var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader );
在这些代码行之后,添加:

console.log("fragment shader:", prefix_fragment + fragmentShader);
console.log("vertex shader:", prefix_vertex + vertexShader);

你可以查看WebGLRenderer的6463和6490行 (https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L6463),里面包含了ThreeJS注入到你的着色器中的标准uniforms/attributes。你可以查看维基百科的相关条目(https://github.com/mrdoob/three.js/wiki - "自定义着色器可用的默认属性/Uniforms/Varyings是哪些?"),但它会指引你到我上面提到的代码行。


1
代码链接已经过期,自答案给出以来该行已经发生了变化。 - klh
@Killah,我会研究并更新这个问题,提供另一个答案。感谢您的提醒。 - Grüse

7

6

这个问题已经得到了很多关注,用户Killah提到现有的答案在当前版本的three.js中不再有效。

因此,我尝试重新解决这个问题,并列出了几个选项:

  • The quickest and easiest way (while not very elegant) is to just put a random error in your shader. You will get a console error with the entire shader code, including everything that three.js adds.

  • The better solution is to output the shader source from where it's compiled, namely THREE.WebGLShader (as of the current three.js version, r68). I've done a quick copy and paste that should output all shader sources before they're compiled.

    Add this after including three.js and before your own code:

    THREE.WebGLShader = ( function () {
        var addLineNumbers = function ( string ) {
            var lines = string.split( '\n' );
            for ( var i = 0; i < lines.length; i ++ ) {
                lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
            }
            return lines.join( '\n' );
        };
        return function ( gl, type, string ) {
            var shader = gl.createShader( type ); 
            console.log(string);
            gl.shaderSource( shader, string );
            gl.compileShader( shader );
            if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {
                console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );
            }
            if ( gl.getShaderInfoLog( shader ) !== '' ) {
                console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ) );
                console.warn( addLineNumbers( string ) );
            }
            return shader;
        };
    } )();
    

    Note that this snippet is just copied (and very slightly changed) from the three.js sources and should be removed before actually using the code. Just for debugging!

  • There is one more option that is less invasive: you can inspect your ShaderMaterial after creating and rendering it at least once, like so:

    var material = new THREE.ShaderMaterial({
        uniforms: {
            uColorMap: { type: 't', value: THREE.ImageUtils.loadTexture('./img/_colors.png') },
            uSpecularMap: { type: 't', value: THREE.ImageUtils.loadTexture('./img/_spec.png') }, 
            uNormalMap: { type: 't', value: THREE.ImageUtils.loadTexture('./img/_normal.png') }
        },
        vertexShader: document.getElementById('vShader').innerText,
        fragmentShader: document.getElementById('fShader').innerText
    });
    

    Then, after rendering the object at least once:

    console.log(material.program.attributes);
    console.log(material.program.uniforms);
    
希望这对大家有所帮助!如果您知道更多和/或更好的获取着色器代码的方法,请随时添加评论。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接