#javascript #three.js #glsl #shader
#javascript #three.js #glsl #шейдер
Вопрос:
Я хочу иметь возможность применять некоторые процедурные структуры к лицам. Первая задача, когда я столкнулся с таким требованием, — создать рекламный щит, на котором нарисован ядерный взрыв в открытом космосе. Я надеялся сделать это в виде анимированного радиального градиента, и мне это частично удалось.
Главное для каждого фрагментного шейдера — иметь доступ к UV
как к единой переменной.
Похоже, что главное в рендеринге спрайтов — это доступ к матрице проекции камеры в вершинном шейдере.
Вот пример http://goo.gl/A7pY01 !
Теперь я хочу нарисовать это на спрайте рекламного щита. Я предполагал использовать THREE.Sprite
для этого with THREE.ShaderMaterial
, но в этом мне не повезло. Казалось, это THREE.SpriteMaterial
только хороший материал для спрайтов. И после проверки некоторого исходного кода я показал, почему спрайты рисуются одним особым способом с использованием плагинов.
Итак, прежде чем я обнаружил, что изобретаю свой собственный велосипед, я почувствовал необходимость спросить людей, как разместить мой собственный пользовательский шейдер на моем собственном пользовательском спрайте без взлома THREE.js ?
Комментарии:
1. Хороший вопрос. Рассмотрите возможность рендеринга вашего шейдера в текстуру, а затем использования текстуры в качестве
map
SpriteMaterial
.2. Да. Я думаю, я сделаю это как-нибудь таким образом, но в этом решении есть несколько недостатков. Я определенно пересмотрю метод рендеринга чуть позже. Ответ на этот вопрос покажет, как обстоят дела в будущем. (Возможно THREE.js нужен какой
THREE.ShaderedSpritePlugin
-то объект.. :))
Ответ №1:
Итак. После небольшого исследования и работы, которые я рассмотрел THREE.ShaderMaterial
, это лучший вариант для выполнения этой небольшой задачи. Благодаря /extras/renderers/plugins/SpritePlugin
, я понял, как формировать и позиционировать спрайты с помощью вершинных шейдеров. У меня все еще есть некоторые вопросы, но я нашел одно хорошее решение.
Чтобы выполнить свою задачу, сначала я создаю простую плоскую геометрию:
var geometry = new THREE.PlaneGeometry( 1, 1 );
И использовать его в сетке с ShaderMaterial
:
uniforms = {
cur_time: {type:"f", value:1.0},
beg_time:{type:"f", value:1.0},
scale:{type: "v3", value:new THREE.Vector3()}
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true,
blending:THREE.AdditiveBlending // It looks like real blast with Additive blending!!!
} );
var mesh = new THREE.Mesh( geometry, material );
Вот мои шейдеры:
Вершинный шейдер:
varying vec2 vUv;
uniform vec3 scale;
void main() {
vUv = uv;
float rotation = 0.0;
vec3 alignedPosition = vec3(position.x * scale.x, position.y * scale.y, position.z*scale.z);
vec2 pos = alignedPosition.xy;
vec2 rotatedPosition;
rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
rotatedPosition.y = sin( rotation ) * alignedPosition.x cos( rotation ) * alignedPosition.y;
vec4 finalPosition;
finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
finalPosition.xy = rotatedPosition;
finalPosition = projectionMatrix * finalPosition;
gl_Position = finalPosition;
}
Я получил вершинный шейдер из исходного кода плагина Sprite и немного изменил его.
Кстати, изменение =
на =
делает экран спрайта липким. Эта вещь отняла у меня много времени.
И это мой фрагментный шейдер:
uniform float cur_time;
uniform float beg_time;
varying vec2 vUv;
void main() {
float full_time = 5000.;
float time_left = cur_time - beg_time;
float expl_step0 = 0.;
float expl_step1 = 0.3;
float expl_max = 1.;
float as0 = 0.;
float as1 = 1.;
float as2 = 0.;
float time_perc = clamp( (time_left / full_time), 0., 1. ) ;
float alphap;
alphap = mix(as0,as1, smoothstep(expl_step0, expl_step1, time_perc));
alphap = mix(alphap,as2, smoothstep(expl_step1, expl_max, time_perc));
vec2 p = vUv;
vec2 c = vec2(0.5, 0.5);
float max_g = 1.;
float dist = length(p - c) * 2. ;
float step1 = 0.;
float step2 = 0.2;
float step3 = 0.3;
vec4 color;
float a0 = 1.;
float a1 = 1.;
float a2 = 0.7;
float a3 = 0.0;
vec4 c0 = vec4(1., 1., 1., a0 * alphap);
vec4 c1 = vec4(0.9, 0.9, 1., a1 * alphap);
vec4 c2 = vec4(0.7, 0.7, 1., a2 * alphap);
vec4 c3 = vec4(0., 0., 0., 0.);
color = mix(c0, c1, smoothstep(step1, step2, dist));
color = mix(color, c2, smoothstep(step2, step3, dist));
color = mix(color, c3, smoothstep(step3, max_g, dist));
gl_FragColor = color;
}
Вот пример того, как создать многоточечный градиент, анимированный по времени. Есть много оптимизаций и несколько мыслей, как сделать это еще более красивым.
Но это почти то, что я хотел.
Комментарии:
1. Было бы неплохо просто иметь возможность присоединять шейдер к SpriteMaterial — но в то же время спасибо за это, хотя, очень полезно!