如何在.obj模型上正确重复纹理?

时间:2019-01-21 06:45:03

标签: opengl webgl

当我将此图片加载到沙发模型上时:

f

我得到了:

sofa

我的纹理参数如下:

COL="collectionName"
HOW_MANY=10000

DATE_CUTOFF=$(mongo <host, user, pass...> dbname --quiet \
--eval "db.$COL.find({}, { createdAt: 1 }).sort({ createdAt: -1 }).skip($HOW_MANY).limit(1)"\
| grep -E -o '(ISODate\(.*?\))')

echo "Copying $HOW_MANY items after $DATE_CUTOFF..."

mongodump <host, user, pass...> -d dbname -c ${COL}\
-q "{ createdAt: { \$gte: $DATE_CUTOFF} }" --gzip

当我将gl.CLAMP_TO_EDGE更改为gl.REPEAT时,

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

即使这张f图片是2的幂,我也会得到相同的浑浊图像。

这是正常行为吗?我想看到这个F在模型上重复出现。像这样:

ideal

1 个答案:

答案 0 :(得分:2)

您需要显示.OBJ加载代码。

在THREE.js中加载相同的文件并应用相同的纹理我得到了

enter image description here

我在this page的底部使用了示例,然后在加载模型后,遍历所有节点并应用了纹理

        const loader = new THREE.TextureLoader();
        const texture = loader.load('resources/images/f-texture.png');
        texture.wrapS = THREE.RepeatWrapping;
        texture.wrapT = THREE.RepeatWrapping;
        root.traverse((node) => {
          if (node.material) {
            if (Array.isArray(node.material)) {
              node.material.forEach((m) => {
                m.map = texture;
              });
            } else {
              node.material.map = texture;
            }
          }
        });

我还快速编写了自己的.OBJ加载器

"use strict";

const vs = `
  uniform mat4 u_worldViewProjection;

  attribute vec4 position;
  attribute vec2 texcoord;

  varying vec2 v_texCoord;

  void main() {
    v_texCoord = texcoord;
    gl_Position = u_worldViewProjection * position;
  }
`;
const fs = `
  precision mediump float;

  varying vec2 v_texCoord;
  uniform sampler2D u_diffuse;

  void main() {
    gl_FragColor = texture2D(u_diffuse, v_texCoord);
  }
`;


function loadTextFile(url) {
  return fetch(url).then(req => req.text());
}

function addModel(context) {
  if (context.faces) {
    const {faces, modelName, materialName} = context;
    context.models.push({
      faces, modelName, materialName,
    });
    context.faces = undefined;
  }
}

function addDataFn(name) {
  return function(context, args) {
    addModel(context);
    const {data} = context;
    if (!data[name]) {
      data[name] = {
        numComponents: args.length,
        data: [],
      };
    }
    data[name].data.push(...args.map(parseFloat));
  };
}

function addPropFn(name) {
  return function(context, args) {
    context[name] = args.join(' ');
  };
}

function addFace(context, args) {
  if (!context.faces) {
    context.faces = [];
  }
  context.faces.push(args.map((vert) => {
    return vert.split('/').map(v => v.length ? parseInt(v) : undefined);
  }));
}

function noop() {
}

const objHandlers = {
  mtllib: addPropFn('mtllib'),
  v: addDataFn('position'),
  vn: addDataFn('normal'),
  vt: addDataFn('texcoord'),
  g: addPropFn('modelName'),
  o: addPropFn('modelName'),
  usemtl: addPropFn('materialName'),
  s: noop,
  f: addFace,
};

function parseObj(objText) {
  const context = {
    data: {},
    models: [],
  };
  objText.split('\n').forEach((origLine, lineNo) => {
    const noCommentLine = origLine.replace(/#.*/, '');
    const line = noCommentLine.trim();
    if (line === '') {
      return;
    }
    const parts = line.split(/\s+/);
    const code = parts.shift();
    const fn = objHandlers[code];
    if (!fn) {
      console.error('unknown code:', code, 'at line', lineNo + 1, ':', line);
    } else {
      fn(context, parts);
    }
  });
  addModel(context);

  const arrays = {};
  const indices = [];
  let numVerts = 0;
  const vertIds = {};
  const arrayNames = Object.keys(context.data);
  for (const [name, src] of Object.entries(context.data)) {
    arrays[name] = {
      numComponents: src.numComponents,
      data: [],
    };
  }

  // for the f statement
  // f v/vt/vn -> position/texcoord/normal
  const channelNames = [
    'position',
    'texcoord',
    'normal',
  ];

  function addVertex(vertexPartIndices) {
    const parts = [];
    vertexPartIndices.forEach((partNdx, ndx) => {
      if (partNdx !== undefined) {
        parts.push(ndx, partNdx);
      }
    });
    const vId = parts.join(',');
    let vertNdx = vertIds[vId];
    if (vertNdx === undefined) {
      vertNdx = numVerts++;
      vertIds[vId] = vertNdx;
      vertexPartIndices.forEach((partNdx, ndx) => {
        if (partNdx === undefined) {
          return;
        }
        const name = channelNames[ndx];
        const data = context.data[name];
        const start = (partNdx - 1) * data.numComponents;
        const end =  start + data.numComponents;
        if (end > data.data.length) {
          debugger;
        }
        const values = data.data.slice(start, end);
        if (values.length !== 3) {
          debugger;
        }
        arrays[name].data.push(...values);
      });
    }
    return vertNdx;
  }

  for (const model of context.models) {
    for (const face of model.faces) {
      const numVerts = face.length;
      if (numVerts < 3) {
        throw new Error('numVerts for face not at least 3');
      }
      if (numVerts > 4) {
        debugger;
      }
      const vNdx0 = addVertex(face[0]);
      for (let i = 1; i < numVerts - 1; ++i) {
        indices.push(vNdx0);
        indices.push(addVertex(face[i]));
        indices.push(addVertex(face[i + 1]));
      }
    }
  }

  arrays.indices = {
    data: new (indices.length > 65535 ? Uint32Array : Uint16Array)(indices),
  };

  return arrays;
}

async function main() {
  const objText = await loadTextFile('models/obj/sofa/ROUND SOFA.obj');
  const arrays = parseObj(objText);

  const m4 = twgl.m4;
  const gl = twgl.getContext(document.querySelector("#c"));
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

  const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

  const tex = twgl.createTexture(gl, {
    src: 'images/f-texture.png',
    flipY: true,
  });

  const uniforms = {
    u_diffuse: tex,
  };

  function render(time) {
    time *= 0.001;
    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    const fov = 30 * Math.PI / 180;
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 1;
    const zFar = 10000;
    const projection = m4.perspective(fov, aspect, zNear, zFar);
    const eye = [500, 2000, -3000];
    const target = [0, 400, 0];
    const up = [0, 1, 0];

    const camera = m4.lookAt(eye, target, up);
    const view = m4.inverse(camera);
    const viewProjection = m4.multiply(projection, view);
    const world = m4.rotationY(time);

    uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);

    gl.useProgram(programInfo.program);
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    twgl.setUniforms(programInfo, uniforms);
    twgl.drawBufferInfo(gl, bufferInfo);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();
body { margin: 0; }
canvas { display: block; width: 100vw; height: 100vh; }
<canvas id="c"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

抱歉,不能包含该模型。但是,它产生了这个

enter image description here

一个完全随机的猜测,在.OBJ文件中查找纹理坐标为3D

看文件本身,我看到纹理坐标是3D

vt -0.7657 0.1621 1.3290
vt -0.7585 0.1439 1.3329
vt 0.2553 0.1439 1.8866
vt 0.2553 0.1621 1.8866
vt 1.2742 0.5898 0.6789
...

代替普通的2D。检查three.js加载代码,它似乎忽略了第三坐标。是否有可能为每个坐标加载所有3个值,但将其索引为2?