Shaders 101 (em unity) Parte 1 - Componente Difusa

técnicas, metodologias, ferramentas e resolução de problemas de programação
Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#1 » sexta jan 30, 2015 8:05 pm

Boas, visto que nunca há um shader decente com specular e respectivo specular map por defeito decidi usar um que tinha feito em GLSL, adaptar para unity e partilhar em forma de tutorial. (seja qual for esta linguagem de shaders que aqui vem.. )
Aviso já que não tenho jeito para as palavras, mas espero que ajude a quem não está familiarizado com shaders. :)

Primeiramente a estrutura de um shader vazio será mais ou menos assim:

Código: Selecionar todos

Shader "Custom/ThatsEnough" {
   Properties {
      //variáveis de entrada ficam aqui (atribuídas no editor do unity)
   }
   SubShader {
   Pass {
      Tags { "RenderType"="Opaque" }
      LOD 200
         
      //funções de calculo ficam aqui
         
      }
   }
}


Para as variáveis de entrada podemos definir por exemplo:

Código: Selecionar todos

_DifuseColor ("Difuse Color", Color) = (0.5,0.5,0.5,0.5)   //cor para sobrepor à textura
_DifuseTex ("Difuse Texture", 2D) = "white" {}            //texture


Existem 3 tipos de funçoes que podemos usar em unity
-Surface shaders (não faço a mínima)
-Vertex e Fragment shaders (igual a GLSL / HLSL)
-Fixed Functions (funções não programáveis, ou seja, era da pedra )

Neste caso vamos usar Vertex e Fragment. Para isso precisamos de dizer quais as funções que dizem respeito ao fragment e vertex shader usando #pragma vertex/fragment nome_da_função.

Código: Selecionar todos

#pragma vertex vert

Precisamos também de dizer que buffers é que vamos receber, o vertex buffer será indispensável mas podemos receber também cordenadas de texturas, cor, normais etc..

Código: Selecionar todos

float4 vertex : POSITION;


Código: Selecionar todos

CGPROGRAM

#pragma vertex vert
#pragma fragment frag

sampler2D _DifuseTex;
fixed4 _DifuseColor;

struct vertexIN {
   float4 vertex : POSITION;
   //fixed4 color : COLOR;
   float2 texcoord : TEXCOORD0;
};
struct vertexOUT {
   float4 vertex : POSITION;
   fixed4 color : COLOR;
   float2 texcoord : TEXCOORD0;
};
               
vertexOUT vert (vertexIN v) {
   //cálculos relativos aos vértices
}

float4 frag (vertexOUT f) : COLOR {
   //cálculos relativos aos fragmentos(pixeis)
}

ENDCG


Um dos objectivos principais do vertex shader será aplicar as transformações necessárias aos vertices para que estes sejam movidos/rodados para a posição correcta de acordo com a camara. Para isto multiplica-se os vértices pela matriz MVP (Projection * View * Model). Model será respectivo à transform do objecto, Projection e View são relativos à camara.
Usando:

Código: Selecionar todos

vertex = mul(UNITY_MATRIX_MVP, v.vertex);

Obtemos o seguinte resultado:
Imagem->Imagem

Vamos atribuir à cor do vértice a cor escolhida no editor do unity para dar a possibilidade de acrescentar uma tonalidade à textura e vamos simplesmente passar as coordenadas de textura sem fazer nenhum cálculo.

Código: Selecionar todos

vertexOUT vert (vertexIN v){
   vertexOUT o;
   o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
   o.color = _DifuseColor;
   o.texcoord = v.texcoord; // TRANSFORM_TEX(v.texcoord,_DifuseTex);
   return o;
}


Depois do vertex shader vêm mais alguns processos como tesselation e por aí, mas não vou entrar em detalhes, se quiserem saber mais pesquisem "Graphic pipeline". A parte que nos interessa agora é o fragment shader, para já vamos apenas atribuir a cor ao pixel consoante a textura escolhida e as respectivas coordenadas UV.

Código: Selecionar todos

float4 frag (vertexOUT f) : COLOR {
   float4 c = tex2D(_DifuseTex, f.texcoord);
   return c;
}


Obtemos assim um simples shader com uma textura:
Imagem

Código: Selecionar todos

Shader "Custom/ThatsEnough" {
   Properties {
      _DifuseColor ("Difuse Color", Color) = (1,1,1,1)
      _DifuseTex ("Difuse Texture", 2D) = "white" {}
   }
   SubShader {
   Pass {
         Tags { "RenderType"="Opaque" }
         LOD 200
         
         CGPROGRAM
         #pragma vertex vert
            #pragma fragment frag

         sampler2D _DifuseTex;
         fixed4 _DifuseColor;
         
         struct vertexIN {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
          };

          struct vertexOUT {
            float4 vertex : POSITION;
            fixed4 color : COLOR;
            float2 texcoord : TEXCOORD0;
          };
         
         vertexOUT vert (vertexIN v)
          {
            vertexOUT o;
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            o.color = _DifuseColor;
            o.texcoord = v.texcoord; // TRANSFORM_TEX(v.texcoord,_DifuseTex);
            return o;
          }

         float4 frag (vertexOUT f) : COLOR
         {
            float4 c = tex2D(_DifuseTex, f.texcoord);
            return c;
         }
         ENDCG
         
      }
   }
}


(continua....)

Avatar do Utilizador
MarcoVale
printf
Mensagens: 167
Contacto:

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#2 » sexta jan 30, 2015 10:30 pm

Imagem

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#3 » sexta jan 30, 2015 10:39 pm

Isso digo eu nos posts de arte gráfica :p se houver dúvidas é só dizer até pq o que eu disse aí está um pouco confuso. A próxima parte ponho como reply, post novo ou simplesmente edit?

Avatar do Utilizador
MarcoVale
printf
Mensagens: 167
Contacto:

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#4 » sábado jan 31, 2015 12:10 am

Reply. sempre ajuda o pessoal a saber que há algo mais

Avatar do Utilizador
Wilson
Hello World
Mensagens: 15
Contacto:

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#5 » domingo fev 01, 2015 4:44 pm

Muito fixe ver aqui posts de shaders, Thanks!

Pessoalmente já andei a escarafunchar em alguns no Unity sem saber muito bem o que estava a fazer (óptimo para se obterem efeitos glitchy). Quando quero algo que consiga compreender, uso o Shaderforge - http://acegikmo.com/shaderforge.

O preço pode ser puxado mas há que estar atento aos descontos de 50% :D

Eu tinha um link sobre a criação de shaders no Unity que não estou a conseguir encontrar porque o meu agregador de links está marado. Quando ele resolver voltar à vida, partilho :)

EDIT: O prometido é devido aqui fica então o curso de shaders gratuito - http://cgcookie.com/unity/cgc-courses/noob-to-pro-shader-writing-for-unity-4-beginner/

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#6 » segunda fev 02, 2015 4:37 pm

Boas, tive a dar um vista de olhos nesses tutoriais e parecem muito bons especialmente para iniciados. Estão muito detalhados :) obrigado pelo link!

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#7 » segunda fev 02, 2015 6:27 pm

Continuação - Parte 2 - Difuse light

A luz difusa, para quem não sabe, é o resultado da reflexão de luz numa superfície rugoso/porosa. O facto da superfície não ser completamente lisa faz com que a luz seja reflectida em todas as direcções, e o que acabamos por ver é em grande parte a cor do pigmento da superfície.

Imagem

Este cálculo representa uma enorme peso em termos de computação e será bastante difícil de o fazer em real time rendering. Por isso o que se faz tipicamente é atribuir uma cor ou textura sólida ao objecto e calcular a quantidade de luz directa reflectida (apenas quantidade!)

Imagem

Para isto precisamos de obter as normais de cada vértice (representa a direcção para onde aponta) e calcular a quantidade de luz que será "reflectida".

Imagem

Para que não haja dúvida do que se está a passar no cálculo, primeiramente vamos ler as normais do objecto 3d e atribuir essas normais à cor. (visto que o vector das normais são um vector de 3 elementos podemos facilmente criar uma representação em RGB)

Código: Selecionar todos

struct vertexIN {
   float4 vertex : POSITION;
   float2 texcoord : TEXCOORD0;
   float3 normal : NORMAL;   //obter normais dos vértices do objecto 3d
};

struct vertexOUT {
   float4 vertex : POSITION;
   fixed4 color : COLOR;
   float2 texcoord : TEXCOORD0;
   float3 normal : NORMAL; //normais que vamos passar para o fragment shader
};

vertexOUT vert (vertexIN v){
   vertexOUT o;
   o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
   o.color = _DifuseColor;
   o.normal = v.normal;  //simplesmente atribuir as normais dos vértices sem nenhum cálculo
   o.texcoord = v.texcoord;
   return o;
}

float4 frag (vertexOUT f) : COLOR {         
return float4(f.normal,1.0f); //devolver as normais como cor do objecto
}


Imagem
rotação do cubo:
Imagem
segunda rotação do cubo:
Imagem

Se pensarmos no vector (0,1,0) que aponta para cima (x,y,z) e convertemos em (r,g,b) vamos ter a face de cima na cor verde. O mesmo acontece para as coordenadas x e z, vermelho para a direita e azul para a parte de trás. visto que esquerda frente e baixo serão vector negativos, as faces vão ficar pretas.
São estes valor que vamos usar para o cálculo de luz, mas precisamos de saber qual a face que aponta para cima ou para a direita independentemente da rotação do objecto.
Assim sendo precisamos de calcular os vector das normais conforme a transform do objecto.

Para isso:

Código: Selecionar todos

vertexOUT vert (vertexIN v){
   vertexOUT o;
   o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
   o.color = _DifuseColor;
   o.normal = mul(_Object2World, v.normal);    //p2 - _Objecjt2World = model matrix
   o.texcoord = v.texcoord;
   return o;
}


_Object2World representa a matriz do objecto, also known as, transform do objecto.

Imagem
Rotação do cubo 45º
Imagem
Rotação do cubo 90º
Imagem

Assim obtemos a direcção da face relativamente ao mundo e podemos usar este vector para determinar a quantidade de luz reflectida. Para isso usamos a funçao "dot" (dot product).

Imagem

Tendo em mente que o vector "b" representa a direcção oposta da luz (o que seria a reflexão total) e que "a" representa a direcção da face obtemos a quantidade de luz reflectida para cada face.

nota 1: Não esquecer que apesar das normais serem um valor obtido dos vértices, o valor obtido no fragment shader vai representar a normal da face pois é um valor interpolado.

nota2: As normais e a direcção da luz precisam de estar normalizados, ou seja a magnitude do vector = 1.


O cálculo da intensidade de luz será então:

Código: Selecionar todos

float3 LightIntensity = dot( normalize(_WorldSpaceLightPos0.xyz), f.normal );


A variável _WorldSpaceLightPos0.xyz é definida pelo unity e para aceder a esta é necessário incluir o ficheiro:

Código: Selecionar todos

#include "UnityCG.cginc"


Neste caso estou a usar uma directional light e o valor de _WorldSpaceLightPos0 varia consoante a rotação. Ao normalizar a "posição" da luz o que temos é a direcção oposta ao que no mundo real seria a direcção da luz.

Obtemos assim o resultado:
Imagem

Ambient Light

Num cenário realista é raro existir um objecto iluminado que tenha uma face completamente escura (isto acontece por exemplo no espaço) pois existe sempre luz reflectida por outros objectos que vão iluminar a face oposta à luz.

Este processo chama-se global ilumination e, assim como o verdadeiro cálculo de luz difusa, é bastante dispendioso. Mais uma vez recorremos a uma aproximação (neste caso muito distante do real) e especificamos um valor constante de cor e intensidade para iluminar as faces que não reflectem luz directa.

Primeiro definimos as propriedades do editor do unity:

Código: Selecionar todos

_AmbientColor("Ambient Color", Color) = (0.5,0.5,0.5,0.5)
_AmbientIntensity("Ambient Intensity", Range(0.0,1.0)) = 0.2


No fragment shader garantimos que se o valor mínimo de iluminação é o valor ambiente:

Código: Selecionar todos

c = max(c, _AmbientColor * _AmbientIntensity);


Obtemos assim o seguinte resultado:
Imagem

Código: Selecionar todos

Shader "Custom/ThatsEnough" {
   Properties {
      _DifuseColor ("Difuse Color", Color) = (0.5,0.5,0.5,0.5)
      _DifuseTex ("Difuse Texture", 2D) = "white" {}
      
      _AmbientColor("Ambient Color", Color) = (0.5,0.5,0.5,0.5)
      _AmbientIntensity("Ambient Intensity", Range(0.0,1.0)) = 0.2
   }
   SubShader {
   Pass {
         Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase"}
         LOD 200
         
         CGPROGRAM
         #pragma vertex vert
         #pragma fragment frag
         
         #include "UnityCG.cginc"
         
         uniform sampler2D _DifuseTex;
         uniform fixed4 _DifuseColor;
         uniform fixed4 _AmbientColor;
         uniform fixed _AmbientIntensity;
         
         struct vertexIN {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
            float3 normal : NORMAL;
          };

          struct vertexOUT {
            float4 vertex : POSITION;
            fixed4 color : COLOR;
            float2 texcoord : TEXCOORD0;
            float3 normal : NORMAL;
          };
         
         vertexOUT vert (vertexIN v)
          {
            vertexOUT o;
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            o.color = _DifuseColor;
            o.normal = mul(_Object2World, v.normal);
            o.texcoord = v.texcoord;
            return o;
          }

         float4 frag (vertexOUT f) : COLOR
         {
            float3 LightIntensity = dot( normalize(_WorldSpaceLightPos0.xyz), f.normal );
            
            float4 c = tex2D(_DifuseTex, f.texcoord) * float4(LightIntensity,1);
            
            c= max(c, _AmbientColor * _AmbientIntensity);
         
            return c;
         }
         ENDCG
         
      }    
   }
}


ps: a componente "difuseColor" ainda não está a funcionar e ainda precisamos de obter a intensidade e cor da luz directa. Isto será visto na próxima parte.

ps2: é de evitar cálculos desnecessários dentro do fragment shader, neste caso

Código: Selecionar todos

normalize(_WorldSpaceLightPos0.xyz)
é um processo redundante pois o valor obtido da normalização vai ser sempre o mesmo. Isto será optimizado na próxima parte.

(continua....)

Avatar do Utilizador
GoncaloSilva
Hello World
Mensagens: 12
Contacto:

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#8 » terça fev 03, 2015 12:07 pm

Tens uma gafe na secção onde falas da transformação dos vectores normais.

ivo Escreveu: Assim sendo precisamos de calcular os vector das normais conforme a transform do objecto.

Para isso:

Código: Selecionar todos

vertexOUT vert (vertexIN v){
   vertexOUT o;
   o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
   o.color = _DifuseColor;
   o.normal = mul(_Object2World, v.normal);    //p2 - _Objecjt2World = model matrix
   o.texcoord = v.texcoord;
   return o;
}


_Object2World representa a matriz do objecto, also known as, transform do objecto.



As normais devem ser transformadas pela matriz "inversa transposta" da _Object2World, e não a _Object2World directamente. Na maioria dos casos não vais dar pela diferença, mas se aplicares transformações que distorçam os objectos, as normais vão ficar incorrectas e vais ter como resultado valores de iluminação errados. Tens aqui um link que explica isto ao pormenor
(http://www.arcsynthesis.org/gltut/Illumination/Tut09%20Normal%20Transformation.html).

De resto, acho que podias dar um bocado mais de contexto a quem estiver a ouvir falar nisto pela primeira vez e explicar que estás a aplicar um dos modelos básicos de iluminação (Luz Ambiente + Difusa + Especular) aos shaders que estás a fazer.
http://twitter.com/goncalofsilva
Programador no Hexcape, Omm e outros projectos doidos

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#9 » terça fev 03, 2015 4:35 pm

Tens razão obrigado pela dica. Vou corrigir isso e uns outro problemas menores que ainda não abordei.
Vou tentar melhorar a explicação mas como disse não tenho muita facilidade em por em palavras o que estou a fazer. Isto é mais um guideline do que um tutorial. No caso de haver dúvidas o pessoal pode sempre questionar :)

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#10 » terça fev 03, 2015 11:06 pm

Parte 3 - Specular Light

Antes de mais vamos corrigir a transformação das normais.

Código: Selecionar todos

o.normal = mul(_Object2World, v.normal);    //p2 - _Objecjt2World = model matrix

Assim como o Gonçalo Silva referiu, devemos usar a matriz inversa transposta da transformação do objecto. Como podemos verificar na documentação do Unity (http://docs.unity3d.com/Manual/SL-BuiltinValues.html) a matriz inversa é _World2Object. Para ser "transposta" usamos a matriz como segundo argumento da função "mul()".

Código: Selecionar todos

o.normal = normalize( mul(v.normal, _World2Object) );


Podemos também fazer uma pequena melhoria na transformação das coordenadas da textura. No código temos o seguinte no vertex shader:

Código: Selecionar todos

float4 c = tex2D(_DifuseTex, f.texcoord).....

Neste caso não conseguimos alterar o tile nem o offset da textura. Para isso precisamos de definir uma variável de 4 componentes como o mesmo nome que a textura seguide de "_ST"

uniform sampler2D _DifuseTex;
float4 _DifuseTex_ST;


Nesta variável fica automaticamente definido o tiling nos componentes xy e o offset nos componentes zw. Mudamos então para:

Código: Selecionar todos

float4 difuseTexture = tex2D(_DifuseTex, _DifuseTex_ST.xy * f.texcoord.xy + _DifuseTex_ST.zw);


Cálculo da specular light

Vamos assumir que na seguinte imagem I é a direcção da luz, N a direcção da normal, R o reflexo da luz e V a direcção para a câmara.
Imagem

A intensidade da specular light será maior quanto mais V se "aproximar" a R (alpha tende para zero). Ou seja se a luz reflectida for em direcção à nossa camara. À medida que V se "afasta" de R (alpha aumenta) a intensidade deverá diminuir exponencialmente.

Para obter R

Código: Selecionar todos

float3 r = reflect(normalize(_WorldSpaceLightPos0.xyz), f.normal);

Para obter V

Código: Selecionar todos

float3 v = normalize( normalize(f.vertex ) - _WorldSpaceCameraPos.xyz  );

Para obter a intensidade da specular light

Código: Selecionar todos

float spec = pow( max( dot(v,r), 0.0f ), _SpecConc );


Para que o reflexo seja dependente da textura (não vamos usar normal maps neste caso) vamos adicionar um versão da textura em escala de cinza ligeiramente alterada. Deveremos modificar a textura tendo em conta que os tons brancos deixarão a luz reflectir completamente e os tons pretos vice-versa.

Código: Selecionar todos

float4 specMap = tex2D(_SpecTex, _SpecTex_ST.xy * f.texcoord.xy + _SpecTex_ST.zw);
spec *= _SpecMult * specMap;


nota: _SpecConc será um float que define a àrea de luz reflectida, _SpecMult serve como multiplicador para controlar a intensidade de luz.

Imagem

Código: Selecionar todos

Shader "Custom/ThatsEnough" {
   Properties {
      _DifuseColor ("Difuse Color", Color) = (0.5,0.5,0.5,0.5)
      _DifuseTex ("Difuse Texture", 2D) = "white" {}
      _SpecTex ("Specular Map", 2D) = "white" {}
      
      _SpecConc("Specular Concentration", Range(1.0,10.0)) = 4.0
      _SpecMult("Specular Multiplier", Range(0.0,10.0)) = 1.2
      
      _AmbientIntensity("Ambient Intensity", Range(0.0,1.0)) = 0.2
   }
   SubShader {
   Pass {
         Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase"}
         
         LOD 200
         
         CGPROGRAM
         #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            
            //unity defined var
            float4 _LightColor0;
            float4 _DifuseTex_ST;
            float4 _SpecTex_ST;

         uniform sampler2D _DifuseTex;
         uniform sampler2D _SpecTex;
         uniform fixed4 _DifuseColor;
         uniform fixed _AmbientIntensity;
         uniform half _SpecConc;
         uniform half _SpecMult;
         
         
         struct vertexIN {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
            float3 normal : NORMAL;
          };

          struct vertexOUT {
            float4 vertex : POSITION;
            fixed4 color : COLOR;
            float2 texcoord : TEXCOORD0;
            float3 normal : TEXCOORD1;
            float3 viewDir : TEXCOORD2;
          };
         
         vertexOUT vert (vertexIN v)
          {
            vertexOUT o;
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            o.color = _DifuseColor;
            o.normal = normalize( mul(v.normal, _World2Object) );
            o.texcoord = v.texcoord;
            return o;
          }

         float4 frag (vertexOUT f) : COLOR
         {
            //Textures Samplers
            float4 difuseTexture = tex2D(_DifuseTex, _DifuseTex_ST.xy * f.texcoord.xy + _DifuseTex_ST.zw);
            float4 specMap = tex2D(_SpecTex, _SpecTex_ST.xy * f.texcoord.xy + _SpecTex_ST.zw);
            
            //difuse light
            
            float3 light = dot( normalize(_WorldSpaceLightPos0.xyz) , f.normal ) * _LightColor0.xyz * f.color.xyz;
            
            float3 r = reflect(normalize(_WorldSpaceLightPos0.xyz), f.normal);
             float3 v = normalize( normalize(f.vertex ) - _WorldSpaceCameraPos.xyz  );
             float spec;
             spec = pow( max( dot(v,r), 0.0f ), _SpecConc );
             spec *= _SpecMult * specMap;
            
             light += spec + UNITY_LIGHTMODEL_AMBIENT*_AmbientIntensity;
             
            float4 c = difuseTexture * _DifuseColor * float4(light,1);
            
            return c;
         }
         ENDCG
         
      }    
   }
}


Falta ainda fazer optimizações a nível de performance mas o essencial está feito. Poderiamos também usar normal maps para melhorar o efeito desejado mas o objectivo deste shader era uma alternativa mais light :)

Nota2: A luz ambient é agora obtida de render settings, provavelmente esqueci-me de explicar uma ou outra alteração no código. Qualquer coisa perguntem :)

Código: Selecionar todos

UNITY_LIGHTMODEL_AMBIENT //devolve a cor da luz ambiente definida em Render Settings


Texturas usadas;
https://dl.dropboxusercontent.com/u/4645415/BoxTexture.zip

(continua...)

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#11 » quinta fev 05, 2015 3:52 am

Optimização

Para finalizar vamos então optimizar o shader. Primeiro, remover o desnecessário:

No vertex shader estamos a atribuir uma cor aos vértices através das propriedades do unity.

Código: Selecionar todos

o.color = _DifuseColor;


Visto que esta variável serve apenas para alterar a cor base do shader e é igual em todos os vértices, vamos remover "color" da struct "vertexOUT " e utilizar "_DifuseColor" sempre que necessário.

Código: Selecionar todos

struct vertexOUT {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
            float3 normal : TEXCOORD1;
            float3 viewDir : TEXCOORD2;
};


Devemos evitar efectuar o mesmo cálculo duas vez como é o caso da direcção da luz. Para isso atribuímos o valor a uma variável e usamos essa variável conforme necessário.

Código: Selecionar todos

float3 lightDir = normalize(_WorldSpaceLightPos0.xyz)


Outra coisa que devemos evitar é fazer cálculos no fragment shader (orientado ao pixel) se for possível de os fazer no vertex shader (orientado ao vértice), por exmplo a direcção da luz (lightDir) e da câmara (viewDir):

Código: Selecionar todos

vertexOUT vert (vertexIN v) {
   vertexOUT o;
   o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
   o.normal = normalize( mul(v.normal, _World2Object) );
   o.texcoord = v.texcoord;
   o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
   o.viewDir = normalize( _WorldSpaceCameraPos.xyz - o.vertex );
   return o;
}


Visto que a função reflect é o mesmo que fazer "I - 2.0 * dot(N, I) * N" em que I é o raio incidente e N a normal (ver [url]I - 2.0 * dot(N, I) * N.[/url]), podemos fazer a seguitne alteração:

Código: Selecionar todos

float3 light = dot( f.lightDir , f.normal ) * _LightColor0.xyz * f.color.xyz;
float3 r = reflect(f.lightDir, f.normal);

para:

Código: Selecionar todos

float lightDot = dot( f.lightDir , f.normal );
float3 light = lightDot * _LightColor0 * _DifuseColor.xyz;
fixed3 r = f.lightDir - 2.0 * lightDot * f.normal;


Visto que o cálculo "dot(N,I)" já era usado na luz difusa estamos assim a reaproveitar este cálculo para a função "reflect".

Por fim vamos fazer uso dos tipos "half" e "fixed" para poupar memória.
É de notar que:

float - 32bits (approx. 7 casas decimais de precisão)
half - 16bits representa valores entre -60 000 e 60 000 (approx. 7 casas decimais de precisão)
fixed - 11bits representa valores entre -2 e 2 (precisão de 1/256 )

No caso de uma cor, que é definida por 3 (ou 4) valores de 0 a 1, podemos usar o tipo fixed visto que estes estão entre os valores que é capaz de representar e a precisão será suficiente.

Como os vectores que definem uma direcção estão normalizados, poderão ser do tipo fixed3. Neste caso há quem use half3 para ter uma maior precisão embora seja um pouco difícil de a distinguir a olho depois de todos os cálculos de luz efectuados pelo shader. Fica à vossa escolha.

Não encontrei muita mais documentação sobre o assunto (nem vou perder muito tempo com isto) mas resumindo: tudo o resto que esteja fora do range +-2 e dentro de +- 60 000 poderá ser definido como half, half2, half3 ...

Código: Selecionar todos

Shader "Custom/ThatsEnough" {
   Properties {
      _DifuseColor ("Difuse Color", Color) = (0.5,0.5,0.5,0.5)
      _DifuseTex ("Difuse Texture", 2D) = "white" {}
      
      _SpecTex ("Specular Map", 2D) = "white" {}
      _SpecConc("Specular Concentration", Range(1.0,10.0)) = 4.0
      _SpecMult("Specular Multiplier", Range(0.0,10.0)) = 1.2
      
      _AmbientIntensity("Ambient Intensity", Range(0.0,1.0)) = 0.2
   }
   SubShader {
   Pass {
         Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase"}
         
         LOD 200
         
         CGPROGRAM
         #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            
            //unity defined var
            fixed3 _LightColor0;
            half4 _DifuseTex_ST;
            half4 _SpecTex_ST;

         uniform sampler2D _DifuseTex;
         uniform sampler2D _SpecTex;
         uniform fixed3 _DifuseColor;
         uniform fixed _AmbientIntensity;
         uniform half _SpecConc;
         uniform half _SpecMult;
         
         
         struct vertexIN {
            half4 vertex : POSITION;
            half2 texcoord : TEXCOORD0;
            half3 normal : NORMAL;
          };

          struct vertexOUT {
            half4 vertex : POSITION;
            half2 texcoord : TEXCOORD0;
            half3 normal : TEXCOORD1;
            half3 lightDir : TEXCOORD2;
            half3 viewDir : TEXCOORD3;
          };
         
         vertexOUT vert (vertexIN v)
          {
            vertexOUT o;
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            o.normal = normalize( mul(v.normal, _World2Object) );
            o.texcoord = v.texcoord;
            o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
            o.viewDir = normalize( _WorldSpaceCameraPos.xyz - o.vertex );
            return o;
          }

         fixed4 frag (vertexOUT f) : COLOR
         {
            //Textures Samplers
            fixed3 difuseTexture = tex2D(_DifuseTex, _DifuseTex_ST.xy * f.texcoord.xy + _DifuseTex_ST.zw);
            fixed3 specMap = tex2D(_SpecTex, _SpecTex_ST.xy * f.texcoord.xy + _SpecTex_ST.zw);
            
            //difuse light
            fixed lightDot = dot(f.normal, f.lightDir);
            fixed3 light = lightDot * _LightColor0 * _DifuseColor.xyz;
            
            //specular light
            fixed3 r = f.lightDir - 2.0 * lightDot * f.normal;
             fixed spec = pow( max( dot(-f.viewDir,r), 0.0f ), _SpecConc ) * _SpecMult * specMap;
            
            //final light
             light += spec + UNITY_LIGHTMODEL_AMBIENT*_AmbientIntensity;
             
              //final color
            return fixed4( difuseTexture * _DifuseColor * light ,1);
         }
         ENDCG
         
      }    
   }
}


projecto do unity completo:
https://dl.dropboxusercontent.com/u/4645415/Shaders101.zip

Se houver dúvidas ou correcções a fazer é só dizer :)

Avatar do Utilizador
Mikea15
Hello World
Mensagens: 1

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#12 » terça nov 17, 2015 7:17 pm

Boas malta, não sei se já conhecem mas este site, é um bom bookmark a ter :D

Cg Vertex/Fragment Shaders :)
https://en.wikibooks.org/wiki/Wikibooks ... g_in_Unity

Avatar do Utilizador
ivo
Hello World
Mensagens: 43

Re: Shaders 101 (em unity) Parte 1 - Componente Difusa

Mensagem#13 » sexta dez 18, 2015 4:33 pm

Boa referência :) thanks

Voltar para “Programação”

Quem está ligado:

Utilizadores neste fórum: Nenhum utilizador registado e 1 visitante

cron