Topaz & ShaderNova
Мне приснилось, я как будто рисовала кристалл во сне, и увидела, как его грани искажают огни города – почти как шейдер, который поёт. Как ты думаешь, сможем ли мы создать свечение, похожее на огранённый драгоценный камень, где каждый луч – маленькое стихотворение света?
Конечно, но не жди, что встроенный глянцевый шейдер справится—настоящим кристаллам нужен френель, который пикирует по краям, карта рефракции, которая преломляет свет на каждой грани, и спекулярное отражение, как будто шепот стихов. Напиши небольшой кусочек GLSL, чтобы получать нормали каждой грани из текстуры, а потом используй смещение, модулированное синусом, для направления лучей, чтобы свечение казалось живым, а не плоским. Или запеки всё это в 3D-текстуру, если хочешь облегчить работу процессору. В любом случае, главное — чтобы каждая грань взаимодействовала со светом, а не просто поблескивала.
Ах, грани шепчут, как драгоценные камни… Давай покажу тебе небольшой ритуал на GLSL, чтобы каждая из них засияла светом.
```
uniform sampler2D facetNormals;
uniform samplerCube envMap;
uniform float time;
in vec3 fragPos;
in vec3 viewDir;
out vec4 fragColor;
void main() {
// Получаем нормаль, принадлежащую этому фрагменту, из текстуры
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec3 facetNormal = texture(facetNormals, uv).rgb * 2.0 - 1.0;
// Резкий эффект Френеля, потому что кристалл любит драматизм
float fresnel = pow(1.0 - max(dot(viewDir, facetNormal), 0.0), 5.0);
// Преломляем свет, как призму
vec3 refractDir = refract(viewDir, facetNormal, 1.0/1.5);
// Добавляем синусоидальную дрожь, чтобы сияние казалось живым
float sineMod = sin(dot(refractDir, vec3(1.0, 0.0, 0.0))*10.0 + time);
refractDir += sineMod * 0.05 * facetNormal;
// Отбираем окружение для мерцающего, почти поэтичного блика
vec3 spec = texture(envMap, refractDir).rgb * fresnel;
fragColor = vec4(spec, 1.0);
}
```
Прикольный трюк, но этот UV-хак просто выжжет грани, если ты не подгонишь разрешение нормалей под разрешение геометрии. Попробуй брать данные из 3D-карты нормалей, привязанной к мешу, или используй поиск в экранном пространстве, если делаешь SSAO. И ещё, эта дрожь в 0.05 утонет в пике Френеля—лучше сделай её более тонкой или модулируй под углом грани. И не забудь прижать refractDir, иначе поиск по карте окружения будет давать артефакты. Просто напоминаю: настоящие кристаллы больше преломляют свет, чем отражают, так что уменьши показатель Френеля, если хочешь получить эту воздушную искру.
Слышала, геометрия что-то намекает. Давай аккуратно впихнем нормали в текстурную карту, чтобы колебаний было совсем чуть-чуть, как румянец жемчужины. Уменьшим показатель Френеля, чтобы свет просачивался сквозь грани, словно туман над озером, и ограничим направление преломления, чтобы отражение окружения не вылезло в другой кристалл. Получится призма, которая поет, а не взрывается.
Вот и правильно – свет должен быть текучим, а не застывшим пламенем. Только помни, фиксировать рефракционный вектор безопасно только если куб-карта бесшовная. Если хочешь добиться этого эффекта туманной скользимости, чуть убавь индекс преломления и, может быть, добавь небольшой сдвиг глубины – около 0.2f – чтобы избежать самопересечений. Тогда у тебя получится кристалл, который действительно будет казаться живым.