Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Problemas outline en Cel Shading

Iniciado por fiknius, 23 de Septiembre de 2009, 11:10:17 PM

« anterior - próximo »

fiknius

Hola a todos, llevo algun tiempo leyendo el foro pero esta es la primera vez que participo activamente jeje..

El caso es que estoy intentando implementar en mi pequeño motor con DirectX 9, el efecto Cartoon mediante HLSL pero no consigo el efecto deseado. Despues de usar un Effect que encontre por ahi creo que consegui el efecto del sombreado Cartoon pero no consigo el Outline.

Pongo el codigo del fichero .fx:
Código (c) [Seleccionar]

//CONSTANTES
int n = 32;

//VARIABLES

// Light intensity
float4 xIntensityAmbient;
float4 xIntensityDiffuse;
float4 xIntensitySpecular;

// Material reflectivity
float4 xReflecAmbient;
float4 xReflecDiffuse;
float4 xReflecSpecular;

// stroke thickness
float xStrokeThinckness;

// light direction (view space)
float3 xLightDir; //  = {0.577, -0.577, 0.577};

// texture
extern texture g_ShadeTex;
extern texture g_ModelTex;

sampler ToonSamp = sampler_state
{
    Texture   = (g_ShadeTex);
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU  = CLAMP;       
    AddressV  = CLAMP;
    AddressW  = CLAMP;
};

sampler MainSamp = sampler_state
{
    Texture   = (g_ModelTex);
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};


// transformations
float4x4 xWorldMatrix      : WORLD;
float4x4 xViewMatrix       : VIEW;
float4x4 xProjMatrix : PROJECTION;

struct VS_OUTPUT
{
    float4 Pos  : POSITION;
    float4 Diff : COLOR0;
    float4 Spec : COLOR1;
    float2 Tex  : TEXCOORD0;
    float2  TexB : TEXCOORD1;
};

VS_OUTPUT VS(
    float3 Pos  : POSITION,
    float3 Norm : NORMAL,
    float2 Tex  : TEXCOORD0)
{
    VS_OUTPUT Out = (VS_OUTPUT)0;
    float3 L = -xLightDir;
    float4x4 WorldView = mul(xWorldMatrix, xViewMatrix);
    float3 P = mul(float4(Pos, 1), (float4x3)WorldView);  // position (view space)
    float3 N = normalize(mul(Norm, (float3x3)xWorldMatrix)); // normal (view space)
    float3 R = normalize(2 * dot(N, L) * N - L);          // reflection vector (view space)
    float3 V = -normalize(P);                             // view direction (view space)
    Out.Pos  = mul(float4(P, 1), xProjMatrix);             // position (projected)
    Out.Diff = xIntensityAmbient * xReflecAmbient + xIntensityDiffuse * xReflecDiffuse * max(0, dot(N, L)); // diffuse + ambient
    Out.Spec = xIntensitySpecular * xReflecSpecular * pow(max(0, dot(R, V)), n/4);   // specular
    Out.Tex  = Tex;
    Out.TexB = Out.Diff.r;                                       
    return Out;
}

VS_OUTPUT OUTLINEVS(
    float3 Pos  : POSITION,
    float3 Norm : NORMAL,
    float2 Tex  : TEXCOORD0)
{
    VS_OUTPUT Out = (VS_OUTPUT)0;
    float3 L = -xLightDir;
    float4x4 WorldView = mul(xWorldMatrix, xViewMatrix);
    float3 N = normalize(mul(Norm, (float3x3)WorldView)); // normal (view space)
    float3 P = mul(float4(Pos, 1), (float4x3)WorldView) + xStrokeThinckness * N;
    float3 R = normalize(2 * dot(N, L) * N - L);          // reflection vector (view space)
    float3 V = -normalize(P);                             // view direction (view space)
    Out.Pos  = mul(float4(P, 1), xProjMatrix);             // position (projected)
    Out.Diff = (0.01,0.01,0.01,1);                                 // diffuse + ambient
    Out.Spec = xIntensitySpecular * xReflecSpecular * pow(max(0, dot(R, V)), n/4);   // specular
    Out.Tex  = Tex;                                       
    return Out;
}

float4 PS(
    float4 Diff : COLOR0,
    float4 Spec : COLOR1,
    float2 Tex  : TEXCOORD0,
    float2 TexB : TEXCOORD1) : COLOR
{
    float2 TexC = (TexB.x,0);
    return tex2D(MainSamp, Tex)* tex2D(ToonSamp,TexB);
}

float4 OUTLINEPS() : COLOR
{
    float4 ReturnThis = (0.1f,0.1f,0.1f,0.1f);
    return ReturnThis;
}

technique CelShading
{
    pass P0
    {
        // shaders
        VertexShader = compile vs_1_1 VS();
        PixelShader  = compile ps_1_1 PS();
    }
   
    pass P1
    {
        VertexShader = compile vs_1_1 OUTLINEVS();
        PixelShader  = compile ps_1_1 OUTLINEPS();
       
        ZEnable = true;
        ZWriteEnable = true;
        CullMode = cw;
AlphaBlendEnable = false;     
    }
}


Sin aplicar el segundo pase obtengo el resultado de Cartoon sin Outline y haciendo la segunda pasada consigo Cartoon con Outline pero no con el efecto deseado. Es como si las caras de los objetos salieran hacia fuera pero en negro y detras se quedara el objeto. Si necesitais mas pistas os puedo mandar unas imagenes.

Muchas gracias de antemano a todos y espero vuestra ayuda.

P.D: Pongo las imagenes para que os hagais un idea mejor.
Esta es sin Outline:


Y esta tiene el Outline 'raro':




XÑA

Por lo que veo este shader lo que hace es lo siguietne:

Primera pasada: dibuja el muñequito con la luz para el efecto cartton

Segunda pasada: 'engrandece' un poquito el muñequito y lo dibuja de un color.

Al realizar la primera pasada, habrás grabado la Z. Por tanto la segunda pasada si no se hiciera un poquito 'grande' el muñeco no se dibujaría nada, porque al estar el pixel ya ocupado, ( y tenido  zfunc=LESS), la tarjeta no vuelve a dibujar sobre el mismo pixel.

Es como si cogieras un dibujo 2D. Lo colocas en la pantalla. Entonces lo haces un poquito más grande y de color negro. Y lo pegas encima del que tenías, pero sin que te borre el de 'abajo'. Entonces te pondrá una silueta alrededor del dibujo.

Bien, quizás tui problema es que tu ZFUNC no es LESS, quizás es LESS_EQUAL y claro, al hacer la segunda pasada,  vuelve a dibujar encima del pixel.

De todas formas, tienes que tener un valor coherente en la variable de extrusión, xStrokeThinckness, que es la que controlará el groso de la línea.

fiknius

Perdona pero soy un poco novato en el tema no entiendo muy bien lo del ZFUNC.
Se supone que ese ZFUNC es un parametro del Device de DirectX o algo asi? lo digo porque en el Shader no lo veo por ningun lado.
He visto que existe esta llamada para el Device (Estoy usando C# para mi motor):
Código (csharp) [Seleccionar]

D3DDevice.RenderState.ZBufferFunction = Compare.Less;

Te refieres a esto ¿no? Actualmente no estoy informando ese valor, asi que supongo que por defecto sera LESS_EQUAL.

En cuanto al xStrokeThinckness no hay problema, le he puesto unas teclas en el interface para hacer crecer o decrecer el valor en tiempo real y asi poder buscar un valor coherente mientras se ejecuta el motor.

Gracias XÑA esta tarde cuando vuelva del trabajo lo pruebo y te comento, un saludo.


blau

Buenas,

por si te sirve de algo para el tema del outline, mejor que hacer dos pasadas, le pasas la normal al pixel shader y haces el producto escalar de la normal con el vector direccion de la camara, si es igual a 0 ese pixel esta en el borde del modelo.

Entonces devuelves negro o el color que quieras para el borde y ya esta.

[modo especulacion on]
Y para el hacer el cartoon shader habia una forma bastante sencilla, si no recuerdo mal se podia  hacer con una textura de una dimension que se indexaba en funcion del producto escalar anterior. Pero no estoy seguro del todo. Tendria que rebuscar en el baul.
[modo especulacion off]

Un saludo.


fiknius

Ese metodo fue el que primero probe, pero creo que no me cogia las texturas de los modelos y como soy muy paquete en el tema de los Shaders  :D, busque otra solución.

La verdad que la solucion que propones parece mas sencilla y entendible para los que estamos un poco peces, si pudieras conseguir algo mas de informacion sobre ello te lo agradeceria, siempre desde la premisa, de que mi nivel de Shaders es bastante bajo.

Todavia me cuesta entender un poco esto de los Shaders  ^_^'

fiknius

#5
Hola XÑA ,
He puesto el Render como me has dicho pero sigue ocurriendo lo mismo:
Estos son todos los parametros del Render que pongo:
Código (csharp) [Seleccionar]

            if (bCartoon)
                m_pD3DDevice.RenderState.Lighting = false; 
            else
                m_pD3DDevice.RenderState.Lighting = m_bRSLigths; 

            m_pD3DDevice.RenderState.ZBufferFunction = Compare.Less;
            m_pD3DDevice.RenderState.VertexBlend = VertexBlend.Disable;
            m_pD3DDevice.RenderState.CullMode = Cull.CounterClockwise;
            m_pD3DDevice.RenderState.ZBufferEnable = true;
            m_pD3DDevice.RenderState.Clipping = true;
            m_pD3DDevice.RenderState.SpecularEnable = true;
            m_pD3DDevice.RenderState.MultiSampleAntiAlias = m_bRSAntialiasing3D;


Tengo algunas variables para cambiar en tiempo de ejecucion una propiedad u otra, no se si veras algo raro.
Un saludo y gracias.

fiknius

Hola otra vez a todos,

Yo sigo con mi batalla jeje... buscando por todos sitios he vuelto a encontrar otro Shader diferente pero ahora me surge otro problema, no se como pasarle la textura del modelo para que combinada con la textura del shader (escala de grises de una dimension) me ponga el color del pixel adecuado, pongo la imagen:



El .fx que estoy utilizando:
Código (c) [Seleccionar]


extern matrix xViewMatrix; // Viewport View Matrix
extern matrix xProjMatrix; // Viewport Projection Matrix
extern matrix xWorldMatrix; // Object position in 3D space
extern matrix xWorldMatrixInverse; // Transpose Matrix 3D space

extern float4 xLightDir; // = float4(0,0,1,0); //our light position

texture xModelTex;


sampler2D texSampler0 : TEXUNIT0 = sampler_state
{
Texture  = (xModelTex);
   MIPFILTER = LINEAR;
   MAGFILTER = LINEAR;
   MINFILTER = LINEAR;
};


//application to vertex structure
struct vin
{
   float4 position   : POSITION0;
   float3 normal  : NORMAL;
   float2 tex0       : TEXCOORD0;
};

//vertex to pixel shader structure
struct vout
{
   float4 position   : POSITION0;
   float2 tex0        : TEXCOORD0;
};


//VERTEX SHADER
void vs( in vin IN, out vout OUT )
{
   //getting to position to object space
   OUT.position = mul(IN.position,mul( mul(xWorldMatrix,xViewMatrix),xProjMatrix));

   //getting the position of the vertex in the world
   float4 posWorld = mul(IN.position, xWorldMatrix);

//transform our normal vector so its facing the right way!
float3 normal   = mul( xWorldMatrixInverse, IN.normal );

//use the light direction & normal to determine the intensity
float diffuse = max(0,dot(-xLightDir,normal));

//use diffuse colour as an offset into our x texture colour
OUT.tex0.x = diffuse;
OUT.tex0.y = 0.0f;
}


//PIXEL SHADER
float4 ps( in vout IN ) : COLOR
{
   //get our colour
   float4 finalcolor = tex2D(texSampler0, IN.tex0);

   //return final pixel colour
return finalcolor;
}


technique CelShading
{
   pass p0
   {
       vertexshader = compile vs_1_1 vs();
       pixelshader  = compile ps_1_4 ps();
   }
}


Aunque creo que este .fx tampoco me soluciona el tema del Outline, que tendria que ver como hacerlo a posteriori, pero por lo menos este Shader parece mas facil de entender.

Un saludo.


fiknius

Ok, muchas gracias Blau, esta tarde cuando salga de trabajar lo probaré.

Saludos.

fiknius

Buenos dias Blau,

Anoche adapte lo que me pasaste de XNA a MDX que es lo que estoy usando en mi  motor, y me sigue pasando lo mismo que la ultima vez, me sale la maya sombreada en grises pero no me coge la textura.

Hoy acabare de instalarme el XNA para ver tu ejemplo en ejecución a ver si depurando paso a paso soy capaz de descubrir lo que estoy haciendo mal.

Saludos.


Vicente

Cita de: fiknius en 29 de Septiembre de 2009, 10:13:47 AM
Anoche adapte lo que me pasaste de XNA a MDX que es lo que estoy usando en mi  motor, y me sigue pasando lo mismo que la ultima vez, me sale la maya sombreada en grises pero no me coge la textura.

Te recomiendo que dejes MDX y te pases a SlimDX o XNA: MDX ya no está soportado y tenía unos cuantos bugs (sobre todo relacionados con memory leaks).

Un saludo!

Vicente

fiknius

Ya me lo habian comentado, pero tengo el motor bastante avanzado y tener que migrarlo a XNA me da un poco de pereza, mas que nada porque ya lo pase de C++ con DirectX9 a C# con DirectX9 y ahora otra vez volver a migrarlo a XNA uuffff...
¿De verdad merece la pena?

Cierto es que me cuesta encontrar ejemplos para MDX y que de XNA hay bastante material.

[EX3]

#12
Hombre, si el motor va ir orientado a Windows, dado que lo estas desarrollando contra DirectX, y el MDX tiene varios errores como comenta Vicente, mejor portarlo hacia un framework estable y en continua evolucion como XNA (de SlimDX no hablo por que apenas lo sigo) que seguir con MDX y arriesgarte a usar el motor y que este empiece a petar por doquier.

Salu2...

P.D.: Tiene gracia que diga esto cuando yo mismo mantengo cierto proyecto en cierto lenguaje y con cierta libreria que no es que sean el paraiso de la estabilidad y buen hacer, ¿verdad, Vicente? :P
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

fiknius

Cita de: blau en 26 de Septiembre de 2009, 09:25:22 PM
Echale un vistazo a esto:

http://aliensofextraordinaryability.com/downloads/CelShaders.zip


Despues de mirarme el codigo y el Shader he intentado reproducirlo en mi motor pero sigo teniendo el mismo problema de siempre, la maldi... textura sigue sin pintarse, no se si es que paso mal el parametro al Shader o que pero el caso es que no soy capaz de conseguirlo, y como muestra...:



Seguire poniendo aqui los avances que vaya teniendo, pero si alguien puede aportarme algo de luz se lo agradecere.
Pongo tambien el Shader por si os da alguna pista mas, este Shader se usa con XNA y no se si tiene que ver algo para que a mi no me funcione en mi motor hecho en Managed DirectX:

Código (cpp) [Seleccionar]

float4x4 xWorldMatrix; //World Matrix
float4x4 xViewMatrix; //View Matrix
float4x4 xProjMatrix; //Projection Matrix

float4 xEyePosition; //Camera Position
float4 xDiffuseLightColor; //Diffuse light color, float4(0.6f, 0.6f, 0.6f, 1.0) is a good starting point
float4 xLightPosition; //Obvious

//the shader has 3 layers, but it works in an good way that mimics
//the way 2D animators paint Cels.
//The base color is constant, and it serves as the shadowed area of the surface.
//Layer one is what would normally be the diffuse are of the surface.

//Layer One is caluculated with a specular function, so the specular hit has to be really wide
float LayerOneSharp = 0.6f; // This sharpens the edges of the layer, valid values are between 0 and 1. 0.6 is a good start
float LayerOneRough = 0.05f; // This sets the bleed of the layer. youd want this pretty wide, so 0.05 is a good start
float LayerOneContrib = 0.05f; // This is a multiplier for the final layer contribution, 0-1 is valid closer to 1 blows out the layer so, 0.05 is good

//Layer two also uses a specular function, it can provide a specular highlight or an additional paint layer
float LayerTwoSharp = 0.85f; //This should be very sharp 0.85 is a good default
float LayerTwoRough = -10.0f; //Highlight should be small 5.0f-10.0f is good start.
float LayerTwoContrib = 0.3f; //0.3 f wil get you pretty close to white without blowing the highlight out. Set it to 0 if you don't want a spec highlight
float EdgeOffset;

//Sampler for the color texture applid to the model
texture xModelTex;
sampler TextureSampler = sampler_state
{
Texture = <xModelTex>;
    AddressU  = WRAP;
    AddressV  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

//Input for the vertex Shader
//There are two vertex shaders both take the same input
struct VS_INPUT
{
float4 Position : POSITION0;
float3 Normal : NORMAL0;
float2 Texcoord : TEXCOORD0;

};

//Output for the Cel vertex shader
struct VS_OUTPUT1
{
float4 Position : POSITION0;
float3 Normal : TEXCOORD1;
float3 ViewDirection : TEXCOORD2;
float3 LightDirection : TEXCOORD3;
float2 Texcoord : TEXCOORD0;
};

//Output for the outline vertex shader
struct VS_OUTPUT2
{
float4 Position : POSITION0;
float4 Normal : TEXCOORD1;
};

//The Cel vertex shader
//It just transforms the model and
//finds the light and view directions
VS_OUTPUT1 Transform(VS_INPUT Input)
{
float4x4 WorldViewProjection = mul(mul(xWorldMatrix, xViewMatrix), xProjMatrix);
float3 ObjectPosition = mul(Input.Position, xWorldMatrix);

VS_OUTPUT1 Output;
Output.Normal = mul(Input.Normal, xWorldMatrix);
Output.Position = mul(Input.Position, WorldViewProjection);

float3 CamPosition;
CamPosition.x = xEyePosition.x;
CamPosition.y = xEyePosition.y;
CamPosition.z = xEyePosition.z;

float3 LightPosition;
LightPosition.x = xLightPosition.x;
LightPosition.y = xLightPosition.y;
LightPosition.z = xLightPosition.z;

Output.ViewDirection = CamPosition - ObjectPosition;
Output.LightDirection = LightPosition - ObjectPosition;
Output.Texcoord = Input.Texcoord;

return Output;
}

//The outline vertex shader
//This tranforms the model and
//"peaks" the surface (scales it out on it's normal)
VS_OUTPUT2 Outline(VS_INPUT Input)
{
float4x4 WorldViewProjection = mul(mul(xWorldMatrix, xViewMatrix), xProjMatrix);

VS_OUTPUT2 Output;
Output.Normal = mul(Input.Normal, xWorldMatrix);
Output.Position = mul(Input.Position, WorldViewProjection)+(mul(EdgeOffset, mul(Input.Normal, WorldViewProjection)));

return Output;
}

//Input for the Cel Pixel shader
struct PS_INPUT
{
float3 Normal : TEXCOORD1;
float3 ViewDirection : TEXCOORD2;
float3 LightDirection : TEXCOORD3;
float2 Texcoord : TEXCOORD0;
};

//The Cel Pixel shader
//This calculates the surface color
float4 Cel(PS_INPUT Input) : COLOR0
{
float3 Normal = normalize(Input.Normal);
float3 ViewDirection = normalize(Input.ViewDirection);
float3 NLightDirection = normalize(Input.LightDirection);

float oneW = 0.18f * ( 1.0f - LayerOneSharp );
float twoW = 0.18f * ( 1.0f - LayerTwoSharp );

float NDotL = dot(Normal, NLightDirection);
float3 Reflection = normalize(2.0f * NDotL * Normal - NLightDirection);

float4 layerOneColor = smoothstep(0.72f-oneW, 0.72f+oneW, pow(saturate(dot(Reflection, ViewDirection)), LayerOneRough));
float4 layerTwoColor = smoothstep(0.72f-twoW, 0.72f+twoW, pow(saturate(dot(Reflection, ViewDirection)), LayerTwoRough));

float4 baseColor = tex2D(TextureSampler, Input.Texcoord) * xDiffuseLightColor;

float4 color = (baseColor + LayerOneContrib*layerOneColor) + LayerTwoContrib*layerTwoColor;
color.a = 1.0;

return color;
}

//This is the ouline pixel shader. It just outputs unlit black.
float4 Black() : COLOR
{
   return float4(0.0f, 0.0f, 0.0f, 1.0f);
}

technique CelShading
{
//Render the Cel shader surface color
pass P0
{
VertexShader = compile vs_3_0 Transform();
PixelShader  = compile ps_3_0 Cel();
CullMode = CCW;
}

//Render the outline surface inverted to create fake edge detection
pass P1
{
VertexShader = compile vs_3_0 Outline();
PixelShader  = compile ps_3_0 Black();
CullMode = CW;
}

}


Gracias blau por el ejemplo.

fiknius

Lo siento, pero me he anticipado en el anterior mensaje jejeje... me pasa por no leer los comentarios, con lo bien que lo explican en el Shader y no me doy ni cuenta, pero ya tengo una primera aproximacion bastante buena a lo que estaba buscando.  :o

A ver que os parece:


Seguire enredando con las variables del Shader hasta que encuentre el toque adecuado.

Muchas gracias a todos.
Saludos.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.