В данном уроке напишем собственный шейдер, который будет накладывать текстуру поверх 3D сцены с прозрачностью и сохранением пропорций изображения. Для этого будем использовать post-processing.
Под нашу задачу уже есть частичное решение в виде класса TexturePass, который работает на основе базового шейдера CopyShader. Данный шейдер может разместить поверх сцены текстуру с прозрачностью, но без сохранения пропорций. Как раз эту проблему мы и будем решать в нашем собственном шейдере.
Для начала добавим в TexturePass возможность передавать свой шейдер.
class TexturePass extends Pass
{
constructor(map, opacity, shader = undefined)
{
super();
if (shader === undefined && CopyShader === undefined)
{
console.error('TexturePass relies on CopyShader');
}
...
}
...
}
Далее создадим класс TexturePassExtend и унаследуем его от TexturePass.
class TexturePassExtend extends TexturePass
{
constructor(map, opacity)
{
super(map, opacity, TexturePassExtendShader);
}
setRenderSize(size)
{
if (this.map === undefined)
return;
const mapWidth = this.map.image.width;
const mapHeight = this.map.image.height;
const mapRatio = mapWidth / mapHeight;
const scaleX = mapWidth / size.x;
const scaleY = (mapWidth / mapRatio) / size.y;
this.uniforms['scaleX'].value = scaleX;
this.uniforms['scaleY'].value = scaleY;
}
}
Здесь мы передаем в TexturePass наш шейдер TexturePassExtendShader и описываем метод setRenderSize для корректировки поведения шейдера при смене размеров холста сцены.
Для написания собственного шейдера за основу возьмем CopyShader. Нас в первую очередь будет интересовать вершинный шейдер.
var TexturePassExtendShader = {
uniforms:
{
"tDiffuse": {value: null},
"opacity": {value: 1.0},
"scaleX": {value: 1},
"scaleY": {value: 1}
},
vertexShader:
[
"uniform float scaleX;",
"uniform float scaleY;",
"varying vec2 vUv;",
"void main() {",
" vUv = uv;",
" gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
" gl_Position.x *= scaleX;",
" gl_Position.y *= scaleY;",
"}"
].join("\n"),
fragmentShader:
[
"uniform sampler2D tDiffuse;",
"uniform float opacity;",
"varying vec2 vUv;",
"void main() {",
" gl_FragColor = texture2D(tDiffuse, vUv);",
" gl_FragColor.a *= opacity;",
"}"
].join("\n")
};
В вершинном шейдере мы корректируем размеры текстуры по ширине и высоте через переменные scaleX и scaleY, которые мы и корректируем в методе setRenderSize с целью сохранения пропорций нашей текстуры.
Надеюсь, что моя статья сподвигнет многих разработчиков пробовать писать собственные решения на языке шейдеров GLSL.
Пример реализации данного шейдера можно посмотреть по ссылке.
Imp5
И убираем водяной знак...
site3d Автор
Спасибо за демонстрацию. В любом случае, цель статьи - показать, как можно такой шейдер написать, а как его использовать - дело другое. Надеюсь, что кому-то будет полезно.
iliazeus
Там и модельку вполне скачать можно; URL есть в
window.site3dWidget_1249.model.path
.site3d Автор
Спасибо за замечание к безопасности кода.
skymal4ik
И сколько реальных % людей может это сделать?) а те, кто могут и будут - ещё меньше :)