Освещение частиц и тени ThreeJS FBO

Обновление: я решил реализовать простое решение для освещения по этой ссылке, http://blog.edankwan.com/post/three-js-advanced-tips-shadow, предложенный ScieCode. Это первая часть связанной страницы. Результат можно увидеть на картинке внизу этого поста.

Связанная страница также содержит информацию о том, как отбрасывать тени с помощью пользовательского шейдерного материала в ThreeJS, но мне кажется, что он больше подходит для мешей и базовых систем частиц. Я работаю с системой частиц FBO.

У меня есть система частиц, использующая FBO. Он построен с использованием шейдерных материалов в ThreeJS. Частицы превращаются в возвышающиеся кучевые облака. Как я могу применить освещение и тени к этим частицам, как если бы они были одной сплошной сеткой?

Ниже представлен последний набор шейдеров, которые отображают частицы на экране:

Честно говоря, я даже не до конца понимаю, как вершинный шейдер выводит позиции. Я нашел этот пример кода и приспособил его для того, что я делаю. Я понимаю большую часть этого, но хвостовая часть вершинного шейдера мне непонятна.

Вершина:

precision highp float;

attribute vec2 id;
attribute vec3 p_color;
attribute float p_size;

uniform vec2 dimensions;
uniform float size;

uniform sampler2D infoTexture;
uniform sampler2D originalTexture;
uniform sampler2D positionTexture;
uniform sampler2D previousPositionTexture;
uniform sampler2D velocityTexture;

varying vec2 vUv;
varying float life;
varying vec2 vSpeed;
varying float vSize;

float modI(float a,float b) {
    float m=a-floor((a+0.5)/b)*b;
    return floor(m+0.5);
}

const float PI = 3.14159265359;

void main() {

    float size_modifier = 1. * p_size;

    float ptr = id.x;
    float u = modI( ptr, dimensions.x ) / dimensions.x;
    float v = ( ptr / dimensions.x ) / dimensions.y;
    vec2 puv = vec2( u, v );

    vec4 velocity = texture2D( velocityTexture, puv );
    // vSpeed = .1 * ( projectionMatrix * modelViewMatrix * vec4( velocity.xyz, 1. ) ).xy;

    vec4 i = texture2D( infoTexture, puv );

    vec4 prev_pos = texture2D( previousPositionTexture, puv );
    vec4 origin_pos = texture2D( originalTexture, puv );

    vec4 c = texture2D( positionTexture, puv );
    vec3 p = c.xyz;
    vUv = uv;
    float life_time = 1000.;
    life = 1. - ( c.a / life_time );
    // if( c.a == 0. ) life = 0.;

    if( velocity.a != 1. ){

        size_modifier = .0;

    }

    vec4 modified = modelViewMatrix * vec4( p, 1. );
    // float a = -atan( vSpeed.y, vSpeed.x ) - .5 * PI;
    // float l = clamp( length( vSpeed ), .5, 4. );
    // mat2 rot = mat2( cos( a ), -sin( a ), sin( a ), cos( a ) );

    // modified.xyz += size * i.x * 10. * vec3( rot * position.xy, 0. );
    // modified.xyz += size * size_modifier * i.x * 10. * vec3( rot * position.xy, 0. );
    modified.xyz += size * size_modifier * i.x * 10. * vec3( position.xy, 0. );
    gl_Position = projectionMatrix * modified;

}

Фрагмент:

precision highp float;

varying float vSize;
varying vec2 vUv;
varying float life;
varying vec2 vSpeed;
varying vec3 p_color_f;

uniform float useTriangles;

const vec2 barycenter = vec2( .5, .6666 );

void main() {

    // render circles
    float d = smoothstep( .5, .55, 1. - 2. * length( vUv - barycenter ) );
    if( d <= 0. ) discard;

    vec3 frag_c = vec3( p_color_f );

    gl_FragColor = vec4( frag_c, 1.);

}

Как я добавил фрагменты шейдера в пользовательский шейдер:

particleMaterial = new THREE.ShaderMaterial( {
    uniforms: THREE.UniformsUtils.merge([
        THREE.UniformsLib.shadowmap,
        THREE.UniformsLib.lights,
        THREE.UniformsLib.ambient,
        {
            size: { type: 'f', value: 1 },
            useTriangles: { type: 'f', value: 0 },
            originalTexture: { type: 't', value: texture },
            infoTexture: { type: 't', value: texture2 },
            positionTexture: { type: 't', value: positionSim.fbos[ 0 ] },
            previousPositionTexture: { type: 't', value: positionSim.fbos[ 1 ] },
            velocityTexture: { type: 't', value: velocitySim.fbos[ 1 ] },
            dimensions: { type: 'v2', value: dimensions },
            cameraPosition: { type: 'v3', value: new THREE.Vector3() },
            prevModelViewMatrix: { type: 'm4', value: new THREE.Matrix4() },
            prevProjectionMatrix: { type: 'm4', value: new THREE.Matrix4() }
        } ]),
    vertexShader: document.getElementById( 'particle-vs' ).textContent,
    fragmentShader: document.getElementById( 'particle-fs' ).textContent,
    transparent: false,
    side: THREE.DoubleSide,
    depthTest: true,
    depthWrite: true,
    lights: true
    // blending: THREE.NoBlending
} );

Основной результат освещения: введите здесь описание изображения

Даже это добавило буре глубины.


person M1ke    schedule 12.05.2019    source источник
comment
Это UniformLibs, а не ShaderChunks, они представляют дополнительные юниформы, которые вы привязываете к шейдеру, они содержат информацию, используемую ShaderChunks. Фрагменты кода — это фрагменты кода, которые импортируются внутри самого шейдера GLSL для облегчения реализации обычных вещей. Вы используете #include <name_of_chunk> внутри своего шейдера для их импорта. пример   -  person ScieCode    schedule 13.05.2019
comment
Хорошо, я забыл об этом, но для моего случая (FBO Particles) я буду работать с простым освещением, как вы видите на картинке. Я заставлю это работать. Если бы я использовал сетки, я бы определенно выбрал маршрут, который вы предложили в вашем первоначальном ответе, и я все это уберу и отмечу ваш ответ как ответ, потому что на самом деле это ответ на то, что я спросил. Могу ли я поговорить с вами наедине о дальнейшем развитии?   -  person M1ke    schedule 13.05.2019
comment
@sciecode discourse.threejs.org, вероятно, самый простой способ. Не стесняйтесь ударить меня.   -  person ScieCode    schedule 13.05.2019


Ответы (1)


Не совсем понятно, как вы хотите, чтобы тени или освещение вели себя. Если бы вы могли сделать это более ясным, я бы с удовольствием отредактировал этот ответ, чтобы лучше ответить на ваши вопросы.

С учетом сказанного, есть два ресурса, которые могут быть жизнеспособными в вашем случае:

  • сообщение в блоге Эдана Квана о тенях для пользовательских шейдеров/частиц. Он немного устарел, но большая его часть все еще актуальна и должна дать вам хорошее представление о проблемах, которые вы пытаетесь решить.
  • Поскольку некоторые блоки шейдеров GLSL изменились со времени создания поста Эдана, я предлагаю вам взглянуть на этот дискурсивный пост, который я сделал, когда пытался сделать что-то похожее на то, о чем вы спрашиваете.

Чтобы смоделировать правильное освещение, вам нужно импортировать light_pars ShaderChunks и все его зависимости. При этом вы можете получить доступ к интенсивности света в каждой позиции вершины частицы. Вы можете использовать эту информацию так, как считаете нужным.

person ScieCode    schedule 13.05.2019
comment
Я уже вижу, что ваш ответ - большая помощь! Я посмотрю на эти ссылки и пойму, что вы имеете в виду о кусках шейдера. Что касается поведения, я просто хочу, чтобы система частиц освещалась так же, как освещается сетка, поэтому, если есть столбец частиц, будет темная сторона и светлая сторона. - person M1ke; 13.05.2019
comment
Я добавил свой последний набор шейдеров, которые фактически помещают частицы на экран. Когда я добавил к этим шейдерам куски шейдера, они перестали работать, и это меня не удивляет. Ошибок нет, но частицы больше не отображаются на экране. - person M1ke; 13.05.2019
comment
Если вы правильно включаете ShaderChunks, они должны либо сломать ваш код и показать ошибку, либо работать правильно. Чтобы знать, как правильно использовать шейдеры, нужно проанализировать их в исходном коде, документации по ним нет. - person ScieCode; 13.05.2019
comment
Что я могу сказать? В консоли нет кодов ошибок или предупреждений, и мои частицы не отображаются на экране. Я не думал, что после внедрения стандартных фрагментов шейдера в шейдеры, которые используют FBO для рендеринга частиц, они бы этого не сделали. Обновил свой пост, чтобы отразить, как я добавлял куски в шейдер. - person M1ke; 13.05.2019
comment
Что ж, для освещения... вы можете рассчитать освещение самостоятельно, просто привяжите юниформу light_position к шейдеру частиц, определите вектор нормалей для каждой частицы и вычислите скалярное произведение, чтобы получить, сколько света касается поверхности этой частицы. Просто не забывайте обновлять эту униформу в каждом кадре, если свет перемещается с течением времени. - person ScieCode; 13.05.2019
comment
Честно говоря, я не знаю, как рассчитать тени самостоятельно, но я следил за сообщением в блоге Эдана, обновляя его в соответствии с тем, что я делаю в сообщении с дискурсом, которое я вам отправил. И все получилось просто отлично. Во второй ссылке есть дополнительная информация, которую я связываю с шейдером под названием UniformsLib. Попробуйте их и посмотрите, изменится ли что-то. Если вы сделаете jsfiddle только с частью вашего кода, отображающей частицы, я смогу дать вам больше подсказок относительно того, что происходит. - person ScieCode; 13.05.2019
comment
Я попытаюсь реализовать базовый метод освещения, как вы предложили, с точечным произведением, и посмотрим, что из этого получится. Я обновлю пост с результатами .. - person M1ke; 13.05.2019