Um único sol
Uma luz distante e um preenchimento constante, sombreados por quanto cada ponto encara o sol de frente.
- 01ObjetivoLuz
- 02TécnicaDifusa (Lambert)
- 03ExecuçãoUm único sol
Parâmetros
Copie para a sua plataforma
- GLSLCopiar
- GodotCopiar
Cadeia de nós
Montada passo a passo, no estilo do GLSL, com os nomes de nós dele.
Montada passo a passo, no estilo do Godot, com os nomes de nós dele.
Este card ainda não tem versão para Unreal Engine 5, então os nós mostram os nomes do GLSL.
Este card ainda não tem versão para Unity, então os nós mostram os nomes do GLSL.
Este card ainda não tem versão para Blender, então os nós mostram os nomes do GLSL.
dotDotProductdotdotdot Normal01 De frente para o sol
maxMaxmaxmaxmax Entrada02 Sem luz negativa
+MultiplyAdd+++ Entrada03 Sol mais ambiente
*Multiply*** Entrada04 Iluminar a cor da superfície
Como funciona
Um sol está tão longe que a luz dele chega em raios paralelos: todo ponto da malha o vê na mesma direção. Quanto dessa
luz um ponto recebe depende só do ângulo entre a superfície e os raios. De frente para o sol, o ponto junta um feixe
inteiro; inclinado, o mesmo feixe se espalha por mais área, e cada pedaço de superfície recebe menos. Esse
espalhamento segue o cosseno do ângulo, que é a lei dos cossenos de LambertO brilho de uma superfície fosca segue o cosseno do ângulo entre a normal e a direção para a luz, e parece o mesmo de qualquer ângulo de visão. Num shader é o produto escalar, cortado em zero, dos dois vetores unitários. Lei dos cossenos de Lambert O brilho de uma superfície fosca segue o cosseno do ângulo entre a normal e a direção para a luz, e parece o mesmo de qualquer ângulo de visão. Num shader é o produto escalar, cortado em zero, dos dois vetores unitários.diffuse = max(N · L, 0)
Passo a passo
- De frente para o sol.
sunDirection()transforma os dois ângulos num vetor unitário que aponta para o sol, e o produto escalarMultiplica dois vetores componente a componente e soma os resultados. Para vetores unitários é o cosseno do ângulo entre eles, de 1 (mesma direção) a -1 (opostos).FundamentoProduto escalar
Multiplica dois vetores componente a componente e soma os resultados. Para vetores unitários é o cosseno do ângulo entre eles, de 1 (mesma direção) a -1 (opostos).
dot(a, b) = |a| |b| cos θFontescom a normalUm vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.- dot (abre em uma nova aba)Referência
- Dot product (abre em uma nova aba)Referência
FundamentoNormal
Um vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.
Fontesda superfície é o cosseno do ângulo entre eles: 1 onde a superfície encara o sol, 0 onde os raios passam rente, negativo do lado oposto. - Sem luz negativa.
max(lambert, 0.0)zera o lado oposto. A prévia do primeiro passo já parecia preta ali, porque a tela não mostra valores abaixo de zero; o corte importa no passo seguinte, onde um valor negativo subtrairia da luz ambiente e abriria um buraco mais escuro do que o lado da sombra deveria ser. - Sol mais ambiente. A cor do sol, escalada pelo fator difuso, mais uma cor ambiente constante. Sombras de verdade não são pretas: o céu e as superfícies próximas rebatem luz para dentro delas. Um termo ambiente constante é o substituto mais barato; um azulado parece céu.
- Iluminar a cor da superfície. A cor própria da superfície (o albedo) multiplica a luz. Uma superfície vermelha reflete a parte vermelha da luz que chega nela, então um sol quente e um ambiente frio tingem de jeitos diferentes o lado iluminado e o lado na sombra.
Tudo aqui é linear, como a iluminação precisa ser: os parâmetros de cor chegam decodificados do sRGB, e o quadro codifica o resultado uma vez, no fim. Multiplicar valores em sRGB escurece a faixa meio iluminada e desloca o terminador.
Gire a prévia: o sombreamento não acompanha a câmera, porque nada nele lê a direção de visão. Essa é a diferença entre luz difusa e luz especular.
Monte no GLSL
A direção do sol é a mesma para todo pixel. No seu próprio renderizador, calcule-a uma vez na CPU e passe-a como um uniform vec3; isso tira 8 das 13 operações.
O botão de copiar entrega um shader de fragmento WebGL2 completo. O seu shader de vértice escreve a normal em espaço
de mundo no varying v_normal (e v_position, v_uv, v_view, que o quadro declara mesmo que este card os
ignore); o main() gerado a normaliza de novo por pixel. Envie os parâmetros de cor como valores lineares:
decodifique antes do sRGB as cores de um seletor, como dizem os comentários do cabeçalho. O main() codifica o
resultado em sRGB uma vez, então desenhe no framebuffer padrão do canvas, não num sRGB, senão a imagem é codificada
duas vezes e fica pálida.
Para usar só a iluminação num shader seu, copie sunDirection() e as linhas de effect(), e troque i.normal pela
sua normal de mundo normalizada. Nada lê a direção de visão nem o tempo, então o resultado é estático e pode ser
gravado numa textura ou em cores de vértice.
Monte no Godot
O shader exportado é unshaded e traz o próprio sol, então o DirectionalLight3D da cena não o afeta. Para que as luzes do Godot conduzam a mesma conta, tire o unshaded, defina ALBEDO como a cor da superfície em fragment() e acrescente uma função light() com DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0, 1.0) * ATTENUATION * LIGHT_COLOR / PI; que é Lambert para cada luz, com sombras via ATTENUATION.
Crie um MeshInstance3D (uma SphereMesh mostra melhor o terminador), dê a ele um novo ShaderMaterial e cole o shader num novo Shader. Os ângulos e as cores do sol aparecem em Shader Parameters no inspetor; o azimute gira em torno do eixo Y do mundo a partir de +Z.
No VisualShader, a cadeia é um nó Input para a normal (leve-a para espaço de mundo com um TransformVecMult por INV_VIEW_MATRIX, ou deixe tudo em espaço de visão e transforme o sol), um Vector3Parameter para a direção do sol, DotProduct, um FloatOp em Max com 0, depois um MultiplyAdd com a cor do sol e a cor ambiente, e um VectorOp Multiply com o albedo na porta Albedo de um material unshaded.
Ainda não há tutorial de Unreal Engine 5 para este card.
Ainda não há tutorial de Unity para este card.
Ainda não há tutorial de Blender para este card.
Código
#version 300 es// Diffuse (Lambert) → Single sun, web GLSL (GLSL ES 3.00). From Creative Center, MIT license.precision highp float;uniform float u_time;uniform vec2 u_resolution; // this pass's output size in pixelsuniform vec3 p_albedo; // Surface color: vec3(0.85, 0.62, 0.42), sRGB; upload it decoded to linearuniform vec3 p_sunColor; // Sun color: vec3(1.0, 0.95, 0.85), sRGB; upload it decoded to linearuniform vec3 p_ambient; // Ambient light: vec3(0.3, 0.33, 0.42), sRGB; upload it decoded to linearuniform float p_azimuth; // Sun azimuth (degrees): -20.0 (-180.0 to 180.0)uniform float p_elevation; // Sun elevation (degrees): 40.0 (-30.0 to 90.0)in vec2 v_uv;in vec3 v_position;in vec3 v_normal;in vec3 v_view;out vec4 fragColor;struct Inputs { vec2 uv; vec3 position; vec3 normal; vec3 view; float time; vec2 fragCoord; vec2 resolution;};vec3 u_toSrgb(vec3 c) { return mix(c * 12.92, 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, c));}// The angles turn the same way as the preview's orbit camera: azimuth around the vertical axis from +Z toward +X,// elevation up from the horizon.vec3 sunDirection(float azimuthDegrees, float elevationDegrees) { float a = radians(azimuthDegrees); float e = radians(elevationDegrees); return vec3(cos(e) * sin(a), sin(e), cos(e) * cos(a));}vec4 effect(Inputs i) { vec3 toSun = sunDirection(p_azimuth, p_elevation); float lambert = dot(i.normal, toSun); float diffuse = max(lambert, 0.0); vec3 lighting = p_sunColor * diffuse + p_ambient; vec3 color = p_albedo * lighting; return vec4(color, 1.0);}void main() { Inputs i; i.uv = v_uv; i.position = v_position; // Interpolation shortens the vectors between vertices i.normal = normalize(v_normal); i.view = normalize(v_view); i.time = u_time; i.fragCoord = gl_FragCoord.xy; i.resolution = u_resolution; vec4 frag_out = effect(i); // The output is sRGB: clamped and encoded once, here, with no tone mapping; opaque, so alpha is 1 fragColor = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), 1.0);}// Diffuse (Lambert) → Single sun, Godot 4. From Creative Center, MIT license.shader_type spatial;// Unshaded shows the effect's color as is, like the site's preview; scene lights don't touch itrender_mode unshaded;uniform vec3 albedo : source_color = vec3(0.85, 0.62, 0.42);uniform vec3 sunColor : source_color = vec3(1.0, 0.95, 0.85);uniform vec3 ambient : source_color = vec3(0.3, 0.33, 0.42);uniform float azimuth : hint_range(-180.0, 180.0, 1.0) = -20.0;uniform float elevation : hint_range(-30.0, 90.0, 1.0) = 40.0;struct Inputs { vec2 uv; vec3 position; vec3 normal; vec3 view; float time; vec2 fragCoord; vec2 resolution;};// The angles turn the same way as the preview's orbit camera: azimuth around the vertical axis from +Z toward +X,// elevation up from the horizon.vec3 sunDirection(float azimuthDegrees, float elevationDegrees) { float a = radians(azimuthDegrees); float e = radians(elevationDegrees); return vec3(cos(e) * sin(a), sin(e), cos(e) * cos(a));}vec4 effect(Inputs i) { vec3 toSun = sunDirection(azimuth, elevation); float lambert = dot(i.normal, toSun); float diffuse = max(lambert, 0.0); vec3 lighting = sunColor * diffuse + ambient; vec3 color = albedo * lighting; return vec4(color, 1.0);}void fragment() { Inputs i; // Godot's UV starts at the top-left; the effect expects GLSL's bottom-left i.uv = vec2(UV.x, 1.0 - UV.y); // VERTEX, NORMAL and VIEW are in view space here; the effect works in world space i.position = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz; i.normal = normalize((INV_VIEW_MATRIX * vec4(NORMAL, 0.0)).xyz); i.view = normalize((INV_VIEW_MATRIX * vec4(VIEW, 0.0)).xyz); i.time = TIME; // FRAGCOORD's origin is the top-left too i.resolution = VIEWPORT_SIZE; i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y); vec4 frag_out = effect(i); ALBEDO = frag_out.rgb;}Ainda não há código de Unreal Engine 5 para este card.
Ainda não há código de Unity para este card.
Ainda não há código de Blender para este card.
Leitura complementar
- Lambertian reflectance (abre em uma nova aba)Referência
- Basic Lighting (abre em uma nova aba)Artigo
- dot (abre em uma nova aba)Referência
- max (abre em uma nova aba)Referência