ThreeJS 预定义着色器属性/制服

Posted

技术标签:

【中文标题】ThreeJS 预定义着色器属性/制服【英文标题】:ThreeJS predefined shader attributes / uniforms 【发布时间】:2013-03-17 19:54:49 【问题描述】:

在做了一些“常规”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 提到现有的答案不再导致当前版本的解决方案三.js。我添加并接受了我自己的答案,请参见下文。

【问题讨论】:

【参考方案1】:

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

【讨论】:

【参考方案2】:

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

在顶点着色器中

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

prefixVertexprefixFragment 变量定义可以在WebGLProgram.jsthree.js 的非缩小版本中找到。

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

【讨论】:

对不起,您(非常有帮助的)答案的最后一部分让我感到困惑。这些变量定义究竟做了什么?你列出了var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader );,但我不确定我们什么时候需要这些。【参考方案3】:

这个问题已经获得了相当多的关注,用户 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;
    ;
 )();

请注意,此 sn-p 只是从 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);

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

【讨论】:

【参考方案4】:

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

三个 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 (https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L6463) 的第 6463 和 6490 行:您将看到三个 JS 在着色器中注入的标准制服/属性。您可以查看 Wiki,在那里您有相关条目(https://github.com/mrdoob/three.js/wiki - 自定义着色器中可以使用哪些默认属性/制服/变量?)但它会将您引导至我上面概述的行。

【讨论】:

代码的链接已过时,自从给出答案后,行已更改。 @Killah 我将研究并用另一个答案更新这个问题。感谢您的提醒

以上是关于ThreeJS 预定义着色器属性/制服的主要内容,如果未能解决你的问题,请参考以下文章

SCNProgram - 如何将“mat4”类型的制服传递给自定义着色器?

未绑定着色器时,制服集和顶点属性值是不是保留

顶点着色器 -> 几何着色器,错误“获取非活动制服的位置”

SpriteKit 着色器未与制服一起运行

如何使用 Swift 将 Float 制服传递给 iOS 金属着色器?

使用制服时 Xamarin OpenGL 片段着色器的奇怪行为