26

在做了一些“常规”WebGL 之后,我开始使用 ThreeJS 的 WebGL 渲染器,没有额外的库 + GLSL 着色器。我现在正在尝试在我的 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

4 回答 4

27

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

在顶点着色器中

"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 );

prefixVertex变量prefixFragment定义可以WebGLProgram.jsthree.js.

编辑:更新到 three.js r.73

于 2013-03-27T17:03:12.570 回答
11

您可以在着色器中使用的制服都取决于您如何设置材质:您是否启用了灯光?顶点颜色?剥皮?...

三个 JS 创建了一个程序,该程序严重依赖于一些定义(代码中的#ifdef),这些定义在程序顶部注入,具体取决于我上面谈到的参数。

我发现了解发生了什么的最好方法是打印三个 JS 生成的着色器:因为您已经了解 GLSL,您将很容易理解代码的含义以及可以使用的制服。在三个 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):您将看到标准制服/属性三个 JS 注入到你的着色器中。您可以查看 Wiki,您可以在其中找到相关条目(https://github.com/mrdoob/three.js/wiki - 自定义着色器中提供哪些默认属性/制服/变量?)但它会指导您到我上面概述的线条。

于 2013-03-27T17:00:40.650 回答
6

这个问题已经获得了相当多的关注,用户 Killah 提到现有的答案在当前版本的 three.js 中不再导致解决方案。这就是我再次尝试解决问题的原因,我想概述一下我发现的几个选项:

  • 最快和最简单的方法(虽然不是很优雅)是在着色器中放置一个随机错误。整个着色器代码都会出现控制台错误,包括 three.js 添加的所有内容。

  • 更好的解决方案是从编译位置输出着色器源,即 THREE.WebGLShader(从当前的 three.js 版本开始,r68)。我已经完成了快速复制和粘贴,它应该在编译之前输出所有着色器源。

    在包含三个.js 之后和您自己的代码之前添加这个:

    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;
        };
    } )();
    

    请注意,此代码段只是从 three.js 源中复制(并且略有更改),应在实际使用代码之前将其删除。只是为了调试!

  • 还有另一种侵入性较小的选项:您可以在创建和渲染至少一次后检查您的 ShaderMaterial,如下所示:

    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
    });
    

    然后,渲染对象至少一次之后:

    console.log(material.program.attributes);
    console.log(material.program.uniforms);
    

希望这对大家有帮助!如果您知道更多和/或更好的方法来获取您的着色器代码,请随时添加您的评论。

于 2014-10-09T16:18:48.347 回答
5

现在可以在 three.js 文档中找到这个问题的答案:https ://threejs.org/docs/#api/renderers/webgl/WebGLProgram

于 2015-04-12T15:22:36.127 回答