gpt4 book ai didi

java - Android OpenGL ES 2.0,为每个三角形计算法线

转载 作者:行者123 更新时间:2023-11-30 00:35:53 25 4
gpt4 key购买 nike

我需要在 Android 中使用 Opengl ES 2.0 为每个三角形面(而不是每个顶点)计算法线。但是我不能直接在 fragment 着色器中传递属性。

找到一个解决方案:为每个三角形重复顶点,并将三角形面法线作为属性传递给顶点着色器

但我不想复制顶点。我正在使用顶点索引绘制三角形。

所以,一个顶点被多个三角形共享,那么我应该如何计算三角形面的法线。

附注我是 opengl 的新手。

最佳答案

最简单的解决方案就是复制顶点。顶点着色器很少成为瓶颈。我不知道您的具体需求,但是,在某些情况下,复制顶点并不是一个好的解决方案。例如,如果网格被蒙皮和动画化,这意味着大量计算发生在顶点着色器中。另一种情况是当网格在顶点着色器中以某种奇怪的方式进行动画处理时,您必须重新计算法线。显然,您无法在顶点着色器中计算每个面的法线。您可以在几何着色器中执行此操作,但我们在 OpenGL ES 2.0 中没有。然而,有一个简单的解决方案——在 fragment 着色器中计算法线!所以,如果顶点复制对你不起作用,这里是解决方案:

  1. 我们将需要一个 OpenGL 扩展 - standard_derivatives,它得到广泛支持,但您仍然需要在运行代码之前检查设备是否支持它。要启用扩展,您必须在代码之前将以下行添加到 fragment 着色器:

    #extension GL_OES_standard_derivatives : enable
  2. 我们需要一个可变变量来表示世界坐标中的顶点位置。它应该在顶点着色器中计算,它是如何完成的很大程度上取决于你的着色器。它用于许多需求,因此您可能已经在顶点着色器中计算了它。所以让我们假设,我们在 fragment 着色器中有这一行:

    varying vec3 positionWorld;
  3. 我们需要相机的 View 矩阵。您可能已经将一个传递给 fragment 着色器。假设我们在 fragment 着色器中有这个制服:

    uniform mat4 viewMatrix;
  4. 现在,我们要计算法线。首先,我们计算 View 空间中的法线,然后转换到世界空间。为了计算视空间中的法线,我们使用导数函数:

    vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));

    此处位置的导数是相对于屏幕空间中的 x 和 y 坐标。这意味着我们有两个位于表面平面中的 vector 。为了使表面正常,我们做叉积。当然,结果不是单位 vector ,所以我们还需要对其进行归一化。

  5. 最后一步是计算世界空间中的法线。 View 矩阵应用从世界空间到 View 空间的变换。人们可能会认为我们需要计算它的逆,因为我们需要从 View 空间转到世界空间,但是因为 View 矩阵是标准正交的,所以该矩阵的转置也是它的逆,所以代码将是:

    vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
  6. 为了让生活更轻松,我们可以将所有内容包装到一个函数中:

    vec3 ReconstructNormal(vec3 positionWorldSpace) 
    {
    vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
    vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
    return normalWorldSpace;
    }

现在我们在世界空间中有了重建的法线。下面,只是一个简单的例子,为什么这会非常有用。请注意,由于它使用 WebGL,因此它也几乎与 OpenGL ES 2.0 兼容。

var container;
var camera, scene, renderer;
var mesh;
var uniforms;

var clock = new THREE.Clock();

init();
animate();

function init() {
container = document.getElementById('container');

camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 0.6;
camera.position.y = 0.2;
camera.rotation.x = -0.45;

scene = new THREE.Scene();

var boxGeometry = new THREE.PlaneGeometry(0.75, 0.75, 32, 32);

var heightMap = THREE.ImageUtils.loadTexture("data:image/png;base64,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");

heightMap.wrapT = heightMap.wrapS = THREE.RepeatWrapping;

uniforms = {u_time: {type: "f", value: 0.0 }, u_heightMap: {type: "t",value:heightMap} };

var material = new THREE.ShaderMaterial({
uniforms: uniforms,
side: THREE.DoubleSide,
wireframe: false,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragment_shader').textContent
});


mesh = new THREE.Mesh(boxGeometry, material);
mesh.rotation.x = 3.14 / 2.0;
scene.add(mesh);

renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0x000000, 1 );
container.appendChild(renderer.domElement);

onWindowResize();

window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize(event) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
requestAnimationFrame(animate);
render();
}

function render() {
var delta = clock.getDelta();
uniforms.u_time.value += delta;
//mesh.rotation.z += delta * 0.5;
renderer.render(scene, camera);
}
    body { margin: 0px; overflow: hidden; }
<script src="https://threejs.org/build/three.min.js"></script>
<div id="container"></div>

<script id="fragment_shader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable

varying vec3 positionWorld; // position of vertex in world coordinates

vec3 ReconstructNormal(vec3 positionWorldSpace)
{
vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
return normalWorldSpace;
}

// Just some example of using a normal. Here we do a really simple shading
void main( void )
{
vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
vec3 normal = ReconstructNormal(positionWorld);
float diffuse = max(dot(lightDir, normal), 0.0);
vec3 albedo = vec3(0.2, 0.4, 0.7);
gl_FragColor = vec4(albedo * diffuse, 1.0);
}
</script>

<script id="vertexShader" type="x-shader/x-vertex">
uniform lowp sampler2D u_heightMap;
uniform float u_time;

varying vec3 positionWorld;

// Example of vertex shader that moves vertices
void main()
{
vec3 pos = position;
vec2 offset1 = vec2(1.0, 0.5) * u_time * 0.01;
vec2 offset2 = vec2(0.5, 1.0) * u_time * 0.01;
float hight1 = texture2D(u_heightMap, uv + offset1).r * 0.02;
float hight2 = texture2D(u_heightMap, uv + offset2).r * 0.02;
pos.z += hight1 + hight2;
vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
positionWorld = mvPosition.xyz;
gl_Position = projectionMatrix * mvPosition;
}
</script>

关于java - Android OpenGL ES 2.0,为每个三角形计算法线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43418131/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com