1

编辑:演示终于上线了:http: //bharling.github.io/deferred/index.html使用下拉菜单切换到环面模型来现场查看问题。注意:需要 Webgl MRT 扩展。

我已经在 WebGL 中开发自己的延迟渲染引擎已有一段时间了,并且已经到了使用 GBuffers 和 MRT 扩展的工作原型的阶段,它可以非常令人满意地渲染一些茶壶。这是从头开始开发的,主要是为了让我在不使用任何框架的情况下正确学习 WebGL,并理解延迟渲染。对于任何感兴趣的人 - 来源在 github 上:https ://github.com/bharling/webgl-defer

我已经到了厌倦了只看到茶壶并尝试为 THREE.js JSON 格式模型实现加载器的阶段。我已经移植(复制)了加载器的主要部分,我可以让网格以正确的顶点和索引缓冲区出现,这很棒,但法线一直很混乱。我选择只支持带有顶点 UV 和顶点法线以及单一材质的索引几何体(最终这应该是基于 PBR 的),所以我忽略 JSON 中的任何其他内容,只将我支持的内容直接写入 Float32Arrays (等) . 下面是我的导入代码,以及我看到的奇怪法线的屏幕截图。

  parseThreeJSModel: (data) =>

    isBitSet = (value, position) ->
      return value & ( 1 << position )

    vertices = data.vertices
    uvs = data.uvs
    indices = []
    normals = data.normals

    vertexNormals = []
    vertexUvs = []
    vertexPositions = []

    @vertexPositionBuffer = new DFIR.Buffer( new Float32Array( data.vertices ), 3, gl.STATIC_DRAW )
    @vertexTextureCoordBuffer = new DFIR.Buffer( new Float32Array( data.uvs[0] ), 2, gl.STATIC_DRAW )

    numUvLayers = data.uvs.length
    faces = data.faces

    zLength = faces.length
    offset = 0

    while offset < zLength
      type = faces[offset++]
      isQuad              = isBitSet( type, 0 )
      hasMaterial         = isBitSet( type, 1 )
      hasFaceVertexUv     = isBitSet( type, 3 )
      hasFaceNormal       = isBitSet( type, 4 )
      hasFaceVertexNormal = isBitSet( type, 5 )
      hasFaceColor       = isBitSet( type, 6 )
      hasFaceVertexColor  = isBitSet( type, 7 )

      if isQuad
        indices.push faces[ offset ]
        indices.push faces[ offset + 1 ]
        indices.push faces[ offset + 3 ]
        indices.push faces[ offset + 1 ]
        indices.push faces[ offset + 2 ]
        indices.push faces[ offset + 3 ]
        offset += 4

        if hasMaterial
          offset++

        if hasFaceVertexUv
          for i in [0 ... numUvLayers] by 1
            uvLayer = data.uvs[i]
            for j in [0 ... 4] by 1
              uvIndex = faces[offset++]
              u = uvLayer[ uvIndex * 2 ]
              v = uvLayer[ uvIndex * 2 + 1 ]

              if j isnt 2 
                vertexUvs.push u
                vertexUvs.push v
              if j isnt 0
                vertexUvs.push u
                vertexUvs.push v

        if hasFaceNormal
          offset++

        if hasFaceVertexNormal
          for i in [0 ... 4] by 1
              normalIndex = faces[ offset++ ] * 3
              normal = [ normalIndex++, normalIndex++, normalIndex ] 
              if i isnt 2
                vertexNormals.push normals[normal[0]]
                vertexNormals.push normals[normal[1]]
                vertexNormals.push normals[normal[2]]
              if i isnt 0
                vertexNormals.push normals[normal[0]]
                vertexNormals.push normals[normal[1]]
                vertexNormals.push normals[normal[2]]

        if hasFaceColor
          offset++

        if hasFaceVertexColor
          offset += 4
      else
        indices.push faces[offset++]
        indices.push faces[offset++]
        indices.push faces[offset++]

        if hasMaterial
          offset++
        if hasFaceVertexUv
          for i in [0 ... numUvLayers] by 1
            uvLayer = data.uvs[i]
            for j in [0 ... 3] by 1
              uvIndex = faces[offset++]
              u = uvLayer[ uvIndex * 2 ]
              v = uvLayer[ uvIndex * 2 + 1 ]
              if j isnt 2 
                vertexUvs.push u
                vertexUvs.push v
              if j isnt 0
                vertexUvs.push u
                vertexUvs.push v

        if hasFaceNormal
          offset++

        if hasFaceVertexNormal
          for i in [0 ... 3] by 1
            normalIndex = faces[ offset++ ] * 3

            vertexNormals.push normals[normalIndex++]
            vertexNormals.push normals[normalIndex++]
            vertexNormals.push normals[normalIndex]

        if hasFaceColor
          offset++

        if hasFaceVertexColor
          offset +=3

    @vertexNormalBuffer = new DFIR.Buffer( new Float32Array( vertexNormals ), 3, gl.STATIC_DRAW )
    @vertexIndexBuffer = new DFIR.Buffer( new Uint16Array( indices ), 1, gl.STATIC_DRAW, gl.ELEMENT_ARRAY_BUFFER )
    @loaded=true

使用 THREE.s 官方导出器从搅拌机导出的简单导入立方体的奇怪法线

上面的屏幕截图应该是扩展的世界空间法线 gbuffer。

我的引擎的一大区别是我不将面部信息存储在类中(例如 THREE.Face3 ),而是将数据直接写入缓冲区属性,更像是 THREE.BufferGeometry。

到目前为止,我一直在使用“学习 WebGL”课程中的犹他茶壶模型,特别是这个链接http://learningwebgl.com/blog/?p=1658。这个模型在我的引擎中工作得很好,据说是三个 JSON 格式的早期版本。我通过将顶点、texcoords 等的 json 数组直接写入 webgl 缓冲区来加载该模型,这在我的引擎中非常有效,但即使是从最新的搅拌机导出器导出的简单立方体似乎也不能很好地工作.

任何建议都非常感谢,谢谢!

编辑:使用 webgl 教程中的茶壶模型的法线截图​​。注意:我并不是说三个导出器坏了,而是我解析它的代码。在过去一年左右的时间里,我在这个引擎中多次使用 GBuffer 实现,现在我很确定这是正确的,我只是在理解三种 json 模型格式时遇到了一些问题。

在此处输入图像描述

4

2 回答 2

1

不确定您在 Three.Face3 到缓冲区之间的转换是否正确。这是我自己的三个 Json 解析器,我用它从搅拌器中加载动画蒙皮网格,这可能对您有所帮助。同样,仅支持部分功能。它返回要与.drawElements而不是一起使用的索引缓冲区.drawArrays

function parsePackedArrayHelper(outArray, index, dataArray, componentSize){
    for (var c = 0; c<componentSize; c++){
        outArray.push(dataArray[index*componentSize + c]);
    }
}

function parseThreeJson(geometry){
    if (isString(geometry)){
        geometry = JSON.parse(geometry);
    }

    var faces = geometry["faces"];
    faces = convertQuadToTrig(faces); // can use the triangulate modifer in blender to skip this 
    // and others data etc... 

    var seenVertices = new Map();
    var currentIndex = 0;
    var maxIndex = 0;

    var c = 0; // current index into the .faces array
    while (c < faces.length){
        var bitInfo = faces[c];
        var hasMaterials = (bitInfo &(1<<1)) !== 0;
        var hasVertexUvs = (bitInfo &(1<<3)) !== 0;
        var hasVertexNormals = (bitInfo &(1<<5)) !== 0;
        var faceIndex = c+1;

        c += (
            4 + //1 for the bitflag and 3 for vertex positions
            (hasMaterials? 1: 0) +
            (hasVertexUvs? 3: 0) +
            (hasVertexNormals ? 3: 0)
        );

        for (var v = 0;v<3;v++){
            var s = 0; 
            var vertIndex = faces[faceIndex+v];
            var uvIndex = -1;
            var normalIndex = -1;
            var materialIndex = -1;
            if (hasMaterials){
                s += 1;
                materialIndex = faces[faceIndex+3];
            }
            if (hasVertexUvs){
                s += 3;
                uvIndex = faces[faceIndex+s+v];
            }
            if (hasVertexNormals){
                s += 3;
                normalIndex = faces[faceIndex+s+v];
            }

            var hash = ""+vertIndex+","+uvIndex+","+normalIndex;
            if (seenVertices.has(hash)){
                indices[currentIndex++] = seenVertices.get(hash);
            } else {
                seenVertices.set(hash, maxIndex);
                indices[currentIndex++] = maxIndex++;
                parsePackedArrayHelper(verticesOut, vertIndex, verticesData, 3);
                if (boneInfluences > 1){
                    // skinning data skipped
                }
                if (hasVertexUvs){
                    parsePackedArrayHelper(uvsOut, uvIndex, uvsData[0], 2);

                    // flip uv vertically; may or may not be needed
                    var lastV = uvsOut[uvsOut.length-1];
                    uvsOut[uvsOut.length-1] = 1.0 - lastV;

                }
                if (hasVertexNormals){
                    parsePackedArrayHelper(normalsOut, normalIndex, normalsData, 3);
                }

                if (hasMaterials){
                    materialIndexOut.push(materialIndex);
                }
            }
        }
    }
}
于 2016-02-15T23:16:01.183 回答
0

它不能直接解决您的问题,但无论如何可能会有所帮助。

如果您有像上面示例中的框这样的原始形状,您还可以THREE.FlatShading一起使用和跳过设置法线。在某些情况下可能更容易:

var material = new THREE.???Material({ shading: THREE.FlatShading });
于 2016-02-15T09:47:58.380 回答