我需要一些关于webgl的帮助。
我必须从代码中打开面部模型(Lee Perry Smith),但我不知道如何识别正确的顶点。
对于我的任务,我不允许使用three.js
。
我试图从搅拌机中获取索引,但由于某种原因我没有运气(它就像搅拌机中识别出的顶点与我为webgl生成的儿子不对应。
有人有任何想法..?
更多信息:
我在搅拌机中使用了这个片段来获取索引:http://blenderscripting.blogspot.it/2011/07/getting-index-of-currently-selected.html
然后进入我的javascript并使用此函数编辑顶点坐标(只是为了看它们是否正确,即使这不是真正想要的转换):
function move_vertex(indices,x,y,z){
vertex = headObject.vertices[0];
indices.forEach(function(index){
vertex[3*index] += x;
vertex[3*index+1]+=y;
vertex[3*index+2]+=z;
});
gl.bindBuffer(gl.ARRAY_BUFFER,headObject.modelVertexBuffer[0]);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertex));
gl.bindBuffer(gl.ARRAY_BUFFER,null);
}
答案 0 :(得分:3)
基本上有无限的方法可以做到这一点。哪一个适合你的情况我不知道。
一个是使用a skinning system。将嘴顶点连接到骨骼上并移动骨骼。
另一种方法是使用变形目标。基本上保持网孔一次开口,一次口闭。在webgl中加载两个网格,将两者都传递到着色器并在它们之间进行渲染
attribute vec4 position1; // data from mouth closed model
attribute vec4 position2; // data from mouth open model
uniform float mixAmount;
uniform mat4 worldViewProjection;
...
// compute the position to use based on the mixAmount
// 0 = close mouth
// 1 = open mouth
// 0.5 = 50% between open and closed mouth etc..
vec4 position = mix(position1, position2, mixAmount);
// use the result in the standard way
gl_Position = worldViewProjection * position;
虽然您希望将结果标准化,但您可以为法线做类似的混音。
大多数建模包支持在包内使用变形目标。它取决于文件格式和导出器是否导出该数据。简单地将一些东西放在一起的方法就是将面部导出两次并使用您拥有的代码加载2个文件。
另一种可能是使用顶点颜色。在您的建模程序中,唇部顶点是一种不同的颜色,然后在代码中按颜色找到这些顶点。
另一种方法是为嘴唇指定不同的材质,然后使用材料找到顶点。
一些3D建模程序允许您将元数据添加到顶点。这基本上是顶点颜色方法的变体。您可能需要编写自己的导出器,因为很少有第三方格式支持额外数据。即使格式理论上可以支持额外的数据,大多数出口商也不会将其输出。
类似地,一些3D建模程序允许您将顶点添加到选择/群集/组中,然后您可以引用这些顶点来查找嘴唇。同样,此方法可能需要您自己的导出器,因为大多数格式都不支持此数据
另一个真正的hacky方式,但将在紧要关头完成工作。选择唇顶点并将它们向右移动1000个单位。然后在你的程序中,你可以找到距离右边太远的所有顶点,并从每个顶点中减去1000个单位,将它们放回原来的位置。这可能会弄乱您的法线,但您可以在之后重新计算法线。
还有一种方法是使用您拥有的数据并编程界面以一次突出显示每个顶点,记下哪些顶点是嘴。
例如,在屏幕上放置<input type="number">
。根据数字对该顶点做一些事情。设置顶点颜色或调整它的位置,你可以做些什么来看它。然后写下哪些顶点是嘴。如果你很幸运,他们会在某个范围内,所以你只需写下第一个和最后一个。
const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector("canvas").getContext("webgl");
const vs = `
attribute vec4 a_position;
attribute vec4 a_normal;
uniform mat4 u_matrix;
varying vec4 v_color;
void main() {
// Multiply the position by the matrix.
gl_Position = u_matrix * a_position;
// Pass the normal as a color to the fragment shader.
v_color = a_normal * .5 + .5;
}
`;
const fs = `
precision mediump float;
// Passed in from the vertex shader.
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
// Yes, this sample is using TWGL (https://twgljs.org).
// You should be able to tell what it's doing from the names
// of the functions and be able to easily translate that to raw WebGL
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
a_position: HeadData.positions,
a_normal: HeadData.normals,
});
const numVertices = bufferInfo.numElements;
let vertexId = 0; // id of vertex we're inspecting
let newVertexId = 251; // id of vertex we want to inspect
// these are normals and get converted to colors in the shader
const black = new Float32Array([-1, -1, -1]);
const red = new Float32Array([ 1, -1, -1]);
const white = new Float32Array([ 1, 1, 1]);
const colors = [
black,
red,
white,
];
const numElem = document.querySelector("#number");
numElem.textContent = newVertexId;
document.querySelector("#prev").addEventListener('click', e => {
newVertexId = (newVertexId + numVertices - 1) % numVertices;
numElem.textContent = newVertexId;
});
document.querySelector("#next").addEventListener('click', e => {
newVertexId = (newVertexId + 1) % numVertices;
numElem.textContent = newVertexId;
});
let frameCount = 0;
function render(time) {
++frameCount;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
// restore old data
// for what's in bufferInfo see
// http://twgljs.org/docs/module-twgl.html#.BufferInfo
const origData = new Float32Array(
HeadData.normals.slice(vertexId * 3, (vertexId + 3) * 3));
const oldOffset = vertexId * 3 * 4; // 4 bytes per float
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_normal.buffer);
gl.bufferSubData(gl.ARRAY_BUFFER, oldOffset, origData);
// set new vertex to a color
const newOffset = newVertexId * 3 * 4; // 4 bytes per float
gl.bufferSubData(
gl.ARRAY_BUFFER,
newOffset,
colors[(frameCount / 3 | 0) % colors.length]);
vertexId = newVertexId;
const fov = 45 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 50;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [0, 0, 25];
const target = [0, 0, 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.identity();
const worldViewProjection = m4.multiply(viewProjection, world);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {
u_matrix: worldViewProjection,
});
gl.drawArrays(gl.TRIANGLES, 0, numVertices);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
&#13;
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
.ui {
position: absolute;
left: 1em;
top: 1em;
background: rgba(0,0,0,0.9);
padding: 1em;
font-size: large;
color: white;
font-family: monospace;
}
#number {
display: inline-block;
text-align: center;
}
&#13;
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/headdata.js"></script>
<canvas></canvas>
<div class="ui">
<button id="prev">⬅</button>
<span>vert ndx:</span><span id="number"></span>
<button id="next">➡</button>
</div>
&#13;