Pular para o conteúdo
Cor · DegradêsIniciante

Céu linear

Uma mistura vertical de um horizonte quente até um zênite profundo, com início e fim que você pode mover.

UVSuperfície
Abrir no Playground
  1. 01ObjetivoCor
  2. 02TécnicaDegradês
  3. 03ExecuçãoCéu linear
Quad de tela cheia

Parâmetros

#fcb880
#294dad
0.10
0.90

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.

  1. i.uv.yUV + VectorDecomposei.uv.yi.uv.yi.uv.y
    UV

    01 Altura no quad

  2. (x - a) / (b - a)Remap(x - a) / (b - a)(x - a) / (b - a)(x - a) / (b - a)

    02 Remapear para a faixa

  3. clampClampclampclampclamp

    03 Limitar entre 0 e 1

  4. mixMixmixmixmix

    04 Misturar as duas cores

Como funciona

Um degradê precisa de um número que vá de 0 a 1 e de duas cores para misturar por ele. Aqui o número vem da altura no quad, e as cores são um horizonte quente e um azul profundo lá em cima.

  1. Altura. i.uv.y é a coordenada vertical da UV: 0 na borda de baixo, 1 na de cima. Mostrada como cor, ela já é um degradê, do preto ao branco.
  2. Remapear. Um céu de verdade não muda de cor por igual do chão até o topo do quadro: ele fica claro perto do horizonte e escurece mais para cima. (height - p_bottom) / (p_top - p_bottom) leva a mistura para uma faixa: 0 na altura de início, 1 na altura de fim. É o inverso de um mix: pergunta quanto do caminho pela faixa o pixel já percorreu.
  3. Limitar. Abaixo do início o remapeamento fica negativo, e acima do fim passa de 1. O clamp o segura nas pontas, então a cor do horizonte preenche tudo abaixo da faixa e a do zênite tudo acima dela. Sem ele, o mix do próximo passo extrapolaria além das duas cores.
  4. Misturar. mix(p_horizon, p_zenith, t) mistura as duas cores canal a canal: vermelho, verde e azul andam cada um em linha reta de uma ponta à outra.

As cores chegam em luz linear, porque o frame decodifica os parâmetros de cor antes de o shader vê-los e codifica o resultado uma única vez no final. Então o mix mistura luz do jeito que luzes sobrepostas se misturam, e o meio continua claro. Misturar direto os valores sRGB de um seletor de cor escureceria o meio; o card Rampa RGB vs OKLab mostra os dois, um sobre o outro.

Aproxime o início e o fim e a faixa vira uma linha de horizonte dura; afaste-os até as bordas e o degradê ocupa a altura toda. Uma tela de oito bits tem só 256 níveis por canal, então um degradê longo e lento entre cores parecidas pode mostrar degraus discretos (banding); um pouquinho de ruído somado antes da saída os esconde.

Monte no GLSL

O código copiado é um shader de fragmento GLSL ES 3.00 completo para WebGL2. Ele lê v_uv do shader de vértice, então desenhe-o em qualquer quad ou triângulo de tela cheia cujo shader de vértice passe UVs de 0 no canto inferior esquerdo a 1 no superior direito. Ele também declara v_position, v_normal e v_view; um shader de vértice 2D pode escrever constantes neles.

  1. Compile-o com um shader de vértice que gere v_uv (e os outros três varyings) e ligue os dois.
  2. Envie os quatro uniforms: p_horizon e p_zenith como vec3 em luz linear, p_bottom e p_top como floats entre 0 e 1. Decodifique o valor sRGB de um seletor em cada canal com c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4.
  3. Desenhe. O main() do shader limita o resultado e o codifica para sRGB sozinho, porque um canvas WebGL2 não faz isso.

Para usar o céu dentro de um shader maior, pegue só o effect() e chame-o com i.uv preenchido; depois codifique uma única vez no fim do seu shader, não dentro do céu.

Monte no Godot

O shader supõe o HDR 2D desligado (o padrão do projeto), então ele mesmo decodifica os uniforms de cor e codifica o resultado. Com Rendering > Viewport > HDR 2D ligado, faça u_toLinear() e u_toSrgb() devolverem c sem mudança.

A exportação é um shader canvas_item, então desenha em qualquer nó 2D.

  1. Adicione um ColorRect e ponha o layout em Full Rect (ou no tamanho do painel que você quer atrás da interface).
  2. Em Material, crie um ShaderMaterial, depois um Shader novo, e cole o código por cima do modelo.
  3. Os quatro uniforms aparecem em Shader Parameters: escolha as cores do horizonte e do zênite e mova o início e o fim entre 0 e 1.

No VisualShader a mesma cadeia é UV → VectorDecompose (y) → Remap (faixa de entrada do início ao fim, saída de 0 a 1) → Clamp → Mix entre dois nós ColorParameter. Para um céu que gira com uma câmera 3D, escreva um shader sky que misture por EYEDIR.y: o mesmo remapeamento e o mesmo mix, outro número no lugar do t.

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

linear-sky.fragGLSL
#version 300 es// Gradients → Linear sky, 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_horizon; // Horizon color: vec3(0.99, 0.72, 0.5), sRGB; upload it decoded to linearuniform vec3 p_zenith; // Zenith color: vec3(0.16, 0.3, 0.68), sRGB; upload it decoded to linearuniform float p_bottom; // Gradient start: 0.1 (0.0 to 0.45)uniform float p_top; // Gradient end: 0.9 (0.55 to 1.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 colors arrive linear, so this mix blends light; mixing their sRGB values instead darkens the middle// (the RGB vs OKLab ramp card shows the difference).vec4 effect(Inputs i) {	float height = i.uv.y;	float ramp = (height - p_bottom) / (p_top - p_bottom);	float t = clamp(ramp, 0.0, 1.0);	vec3 sky = mix(p_horizon, p_zenith, t);	return vec4(sky, 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);}
linear-sky.gdshaderShader do Godot
// Gradients → Linear sky, Godot 4. From Creative Center, MIT license.shader_type canvas_item;// Unshaded shows the effect's color as is, like the site's preview; scene lights don't touch itrender_mode unshaded;uniform vec3 horizon : source_color = vec3(0.99, 0.72, 0.5);uniform vec3 zenith : source_color = vec3(0.16, 0.3, 0.68);uniform float bottom : hint_range(0.0, 0.45, 0.01) = 0.1;uniform float top : hint_range(0.55, 1.0, 0.01) = 0.9;struct Inputs {	vec2 uv;	vec3 position;	vec3 normal;	vec3 view;	float time;	vec2 fragCoord;	vec2 resolution;};// Godot's 2D renderer works in sRGB (HDR 2D off, the default), so the frame decodes color params and sRGB// textures to linear itself and encodes the result. With HDR 2D on, make these return c unchanged.vec3 u_toLinear(vec3 c) {	return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));}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 colors arrive linear, so this mix blends light; mixing their sRGB values instead darkens the middle// (the RGB vs OKLab ramp card shows the difference).vec4 effect(Inputs i) {	float height = i.uv.y;	float ramp = (height - bottom) / (top - bottom);	float t = clamp(ramp, 0.0, 1.0);	vec3 sky = mix(u_toLinear(horizon), u_toLinear(zenith), t);	return vec4(sky, 1.0);}void fragment() {	Inputs i;	// UV starts at the top-left; the effect expects GLSL's bottom-left	i.uv = vec2(UV.x, 1.0 - UV.y);	// A flat quad facing the viewer, the same as the site's 2D preview	i.position = vec3(i.uv * 2.0 - 1.0, 0.0);	i.normal = vec3(0.0, 0.0, 1.0);	i.view = vec3(0.0, 0.0, 1.0);	i.time = TIME;	// FRAGCOORD's origin is the top-left too	i.resolution = 1.0 / SCREEN_PIXEL_SIZE;	i.fragCoord = vec2(FRAGCOORD.x, i.resolution.y - FRAGCOORD.y);	vec4 frag_out = effect(i);	COLOR = vec4(u_toSrgb(clamp(frag_out.rgb, 0.0, 1.0)), 1.0);}

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