Я сейчас работаю над симуляцией жидкости. Я работаю в 3D, как и входы и выходы. Каждый шейдер берет один или несколько трехмерных образцов и в идеале должен выводить трехмерные данные.
В настоящее время я нарезаю трехмерный куб и запускаю шейдер на каждой плоскости. Этот метод работает, но затем мне нужно скопировать данные из каждой 2D текстуры в CPU, чтобы восстановить 3D текстуру и отправить ее обратно в GPU. Шаг копирования ужасно медленный, и я думаю, что этот метод не оптимален.
const vertexShaderPlane = `#version 300 es
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
in vec3 position;
out vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position.xy, 0., 1. );
}
`
const fragmentShaderPlane = `#version 300 es
precision highp float;
precision highp sampler3D;
uniform float uZ;
in vec3 vPosition;
out vec4 out_FragColor;
void main() {
out_FragColor = vec4(vPosition.xy, uZ, 1.);
}`
const vertexShaderCube = `#version 300 es
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
in vec3 position;
out vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`
const fragmentShaderCube = `#version 300 es
precision highp float;
precision highp sampler3D;
uniform sampler3D sBuffer;
in vec3 vPosition;
out vec4 out_FragColor;
void main() {
vec4 data = texture(sBuffer, vec3(vPosition));
out_FragColor = vec4(data);
}
`
const canvas = document.createElement('canvas')
const context = canvas.getContext('webgl2', { alpha: false, antialias: false })
const scene = new THREE.Scene()
const renderer = new THREE.WebGLRenderer({ canvas, context })
const cameras = {
perspective: new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 50000),
texture: new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5, 0, 1)
}
renderer.autoClear = false
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
// cameras.perspective.position.set(2, 2, 2)
document.body.appendChild(renderer.domElement)
// Uniforms
const planeUniforms = { uZ: { value: 0.0 } }
const cubeUniforms = { sBuffer: { value: null } }
// Plane (2D)
const materialPlane = new THREE.RawShaderMaterial({
uniforms: planeUniforms,
vertexShader: vertexShaderPlane,
fragmentShader: fragmentShaderPlane,
depthTest: true,
depthWrite: true
})
const planeGeometry = new THREE.BufferGeometry()
const vertices = new Float32Array([
0, 0, 0,
1, 0, 0,
1, 1, 0,
1, 1, 0,
0, 1, 0,
0, 0, 0
])
planeGeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
const plane = new THREE.Mesh(planeGeometry, materialPlane)
plane.position.set(-0.5, -0.5, -0.5)
scene.add(plane)
// Cube (3D)
const materialCube = new THREE.RawShaderMaterial({
uniforms: cubeUniforms,
vertexShader: vertexShaderCube,
fragmentShader: fragmentShaderCube,
depthTest: true,
depthWrite: true,
visible: false
})
const cube = new THREE.Group()
for (let x = 0; x < 32; x++) {
const offset = x / 32
const geometry = new THREE.BufferGeometry()
const vertices = new Float32Array([
0, 0, offset,
1, 0, offset,
1, 1, offset,
1, 1, offset,
0, 1, offset,
0, 0, offset
])
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
const mesh = new THREE.Mesh(geometry, materialCube)
cube.add(mesh)
}
cube.position.set(-0.5, 0, -2)
cube.scale.set(0.5, 0.5, 0.5)
cube.rotation.set(1, 1, 1)
scene.add(cube)
// Computing Step
const texture2D = new THREE.WebGLRenderTarget(32, 32, { type: THREE.FloatType })
const planeSize = (32 ** 2 * 4)
const pixelBuffers = Array.from(Array(32), () => new Float32Array(planeSize))
const data = new Float32Array(planeSize * 32)
renderer.setRenderTarget(texture2D)
for (let i = 0; i < 32; i++) {
materialPlane.uniforms.uZ.value = i / 32
renderer.render(scene, cameras.texture)
renderer.readRenderTargetPixels(texture2D, 0, 0, 32, 32, pixelBuffers[i]) // SLOW PART
data.set(pixelBuffers[i], i * planeSize)
}
const texture3D = new THREE.DataTexture3D(data, 32, 32, 32)
texture3D.format = THREE.RGBAFormat
texture3D.type = THREE.FloatType
texture3D.unpackAlignment = 1
materialPlane.visible = false
// Display Step
materialCube.visible = true
cubeUniforms.sBuffer.value = texture3D
renderer.setRenderTarget(null)
renderer.render(scene, cameras.perspective)
<script src="https://threejs.org/build/three.min.js"></script>
Подчеркну, что рендеринг работает . Это просто очень медленно , потому что мне нужно выполнить дюжину шейдеров.
Потенциальные решения, которые я нашел, следующие:
- Используйте функцию
render.copyFramebufferToTexture
для скопируйте данные непосредственно в новую текстуру. К сожалению, я думаю, что это работает только для 2D, а не 3D текстур. - Используйте веб-работников, чтобы разделить задачу и отобразить план для каждого работника. Однако, чтобы передать данные работникам, вы должны скопировать их, что возвращает к первоначальной проблеме. Передача данных также не будет эффективной, поскольку, пока один работник будет выполнять свою работу, другие не будут иметь доступа к данным.
РЕДАКТИРОВАТЬ:
Я просто ищу способ ускорить процесс копирования 2D-данных в ЦП, чтобы вернуть 3D-текстуру обратно в графический процессор.
Реальная проблема - renderer.readRenderTargetPixels
, которая действительно замедляет мой рендеринг .