Microsoft MVP

Email y Rss

email rss

Klout

Seguidores en facebook

Timeline de mi Twitter

Tienes preguntas?

Ideas de un Conejo
Más allá de los sistemas de información: (C#)=> videojuegos, soluciones a problemas interesantes y Sistemas Operativos
XNA
C#
Sistemas Operativos
Varios
Windows Phone
WinRT
XAML
Azure
HTML 5
Acerca de

C# – XNA – Shaders – Como Convertir Una Imagen a Escala de Grises

December 23rd, 2009 by JuanK

Follow @JuanKRuiz

Hola, continuando con el tema del procesamiento de imágenes, este es el tercer artículo relacionado con la conversión de una imagen a escala de grises.

 

En los dos artículos anteriores:

http://juank.black-byte.com/xna-convertir-imagen-escala-grises/

http://juank.black-byte.com/c-bitmap-convertir-imagen-escala-grises/

 

Se reviso como convertir una imagen a escala de grises utilizando XNA/Texture2D y también como hacerlo utilizando un objeto Bitmap del .net Framework.

 

Sin embargo hubo un tema que no toqué y honestamente no pretendía tocar, al menos no por ahora, pero uno de mis lectores dejo sembrada en mi esa inquietud, y aquí lo tengo este artículo que muestra como convertir una imagen en escala de grises utilizando el método que es, de lejos, el más eficiente de todos y también el más sencillo una vez se sabe acerca de los Shaders.

 

Como trabajar los Shaders en XNA?

 

Para programar el shader he preparado un código de ejemplo inicial, el cual sencillamente crea un rectángulo y lo pone en un entorno 3D y le aplica una textura, la cual será a la que le modificaremos la información por medio del shader. Acá esta el código inicial sin Shaders.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BlogHLSL
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        VertexPositionTexture[] verts;
        BasicEffect effect;
        Texture2D textura;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            
            GraphicsDevice.RenderState.CullMode = CullMode.None;
            GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);

            
            effect = new BasicEffect(GraphicsDevice, null);
            effect.View = Matrix.CreateLookAt(new Vector3(0,0,-10), new Vector3(0,0,1), Vector3.Up);
            effect.Projection = Matrix.CreatePerspectiveFieldOfView(0.5f, 
                                               Window.ClientBounds.Width/ Window.ClientBounds.Height, 
                                               1, 10);
            effect.World = Matrix.Identity * Matrix.CreateTranslation(1.7f,1.7f,0);
            effect.TextureEnabled = true;

            base.Initialize();
        }

        protected override void LoadContent()
        {
            textura = Content.Load<texture2d>("rabbid");
            effect.Texture = textura;

            verts = new VertexPositionTexture[4];
            verts[0] = new VertexPositionTexture(new Vector3(-1,1,0),      new Vector2(0,0));
            verts[1] = new VertexPositionTexture(new Vector3(1, 1, 0),     new Vector2(1, 0));
            verts[2] = new VertexPositionTexture(new Vector3(-1, -2.7f, 0),new Vector2(0, 1));
            verts[3] = new VertexPositionTexture(new Vector3(1, -2.7f, 0), new Vector2(1, 1));            
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                GraphicsDevice.DrawUserPrimitives<vertexpositiontexture>(PrimitiveType.TriangleStrip, verts, 0, 2);
                pass.End();
            }
            effect.End();
            base.Draw(gameTime);
        }
    }
}

 

Ahora a programar con Shaders!!!

 

Tomando el código anterior como base lo que haré será dibujar la textura en otra posición, simplemente cambiando la posicion del world, la diferencia será que el efecto que usaré para dibujar la textura no será un BasicEffect sino un Effect.

Al utilizar Effect se tiene todo el poder de los Shaders en las manos, pero se debe asumir un pequeño costo y es que dejar la aplicación haciendo exactamente lo que hacia es un poco más complicado de hacer de lo que era con un BasicEffect , si bien la funcionalidad del BasicEffect se puede considerar muy limitada.

Shader para dibujar una textura

BasicEffect es un shader pre implementado que viene con XNA el cual sencillamente nos permite comenzar a trabajar sin preocuparnos por saber o no de Shaders. En su lugar vamos a trabajar con Effect. Para inicializar Effect necesitamos un archivo .fx que es el que contendrá el código del shader, así que en el Content se debe agregar un nuevo archivo de tipo Effect y lo llamaré Efecto.fx.

Este es el shader con el que iniciaré a trabajar, desde luego esto va en el archivo .fx, es decir este es el Shader mínimo que permite hacer exactamente lo mismo que con BasicEffect:

float4x4 WorldViewProjection;
Texture textura;
sampler muestreador = sampler_state
{
  Texture = <textura>;    
  magfilter = LINEAR; 
  minfilter = LINEAR; 
  mipfilter = LINEAR;
  AddressU = mirror; 
  AddressV = mirror;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 Tex : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 Tex : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{    
    VertexShaderOutput output = (VertexShaderOutput)0;
    output.Position = mul(input.Position , WorldViewProjection);
    output.Tex= input.Tex;
    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    return tex2D(muestreador, input.Tex.xy);
}

technique Technique1
{
    pass Pass1
    {
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

 

No es complicado entender el código pero no entrare en mayores detalles porque recuerden que mi intención no es hacer un curso de Shaders, ya habrá tiempo para ello, por ahora el objetivo es aprender a aplicar el efecto de escala de grises y brindaré la información suficiente para que el artículo sea suficientemente entendible para todos.

Una vez hecho el shader se debe preparar el código para utilizarlo, así que se debe declarar una referencia a un objeto Effect el cual he llamado shaderEffect, y en el método LoadContent adiciono este código de inicialización justo después de cargar la textura:

            Matrix tmpWorld = Matrix.Identity * Matrix.CreateTranslation(-0.5f, 1.7f, 0);
            Matrix tmpView = Matrix.CreateLookAt(new Vector3(0,0,-10), new Vector3(0,0,1), Vector3.Up);
            Matrix tmpProjection = Matrix.CreatePerspectiveFieldOfView(0.5f, 
                                               Window.ClientBounds.Width/ Window.ClientBounds.Height, 
                                               1, 10);

            shaderEffect = Content.Load<effect>("Efecto");
            shaderEffect.CurrentTechnique = shaderEffect.Techniques["Technique1"];
            shaderEffect.Parameters["WorldViewProjection"].SetValue(tmpWorld * tmpView * tmpProjection);
            shaderEffect.Parameters["textura"].SetValue(textura);

 

Prácticamente es el mismo código que para BasicEffect pero con algunas diferencias menores, es importante recordar que el world ha sido inicializado con una traslación diferente para que la imagen de la textura quede justo al frente de la original. Otra cosa importante de notar es que a diferencia de los dos artículos anteriores donde lo que hice fue crear una copia de la textura, en este la copia solo se realiza a nivel de la memoria de video por lo cual no crearemos un nuevo Texture2D. (Esto en realidad es lo que lo hace increíblemente más rápido ya que todo el procesamiento se hace en la GPU)

El código para dibujar en el método draw es básicamente replicar el anterior, adiciono esto justo debajo de effect.End():

            shaderEffect.Begin();
            foreach (EffectPass pass in shaderEffect.CurrentTechnique.Passes)
            {
                pass.Begin();
                GraphicsDevice.DrawUserPrimitives<vertexpositiontexture>(PrimitiveType.TriangleStrip, verts, 0, 2);
                pass.End();
            }
            shaderEffect.End();

 

Ahora tengo esto:

image

Shader para aplicar el efecto de escala grises

Tal como lo mostré en los artículos anteriores, para convertir una imagen en escala de grises basta con sumar los componentes de color de cada pixel y distribuir la intensidad de color de manera proporcional a como nuestro ojo percibe los diferentes componentes de color, esto es 0.3R 0.59G 0.11B. Para hacer esto desde el shader vasta con modificar el PixelShader (en este caso PixelShaderFunction)de la siguiente forma:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    float4 color = tex2D(muestreador, input.Tex.xy);
    color.rgb = color.r * 0.3 + color.g * 0.59 + color.b *.11;
    return color;
}

 

Y Listo! se ejecuta el programa y…:

image

Hasta pronto!

Eso es todo!!

En estos otros artículos muestro como hacer lo mismo pero con XNA y Texture 2D, y como hacerlo utilizando Bitmap y PictureBox:

Con XNA y Texture 2D= http://juank.black-byte.com/xna-convertir-imagen-escala-grises/
Con XNA y Texture 2D= http://juank.black-byte.com/xna-convertir-imagen-escala-grises/






Print Friendly

Follow @JuanKRuiz

  • 5 Comentarios »
  • Publicado en la categoría 'C#, XNA'

5 comentarios to “C# – XNA – Shaders – Como Convertir Una Imagen a Escala de Grises ”


  • Anonymous Says:
    December 23rd, 2009 at 10:54 am  

    [...] [...]

  • Ideas de un Conejo Says:
    December 23rd, 2009 at 10:56 am  

    [...] Les comparto este artículo escrito originalmente en mi blog:http://juank.black-byte.com/xna-shaders-convertir-imagen-grises/———- [...]

  • BernieR Says:
    December 23rd, 2009 at 12:14 pm  

    Hello,
    Thanks for article. Everytime like to read you.

  • C# - XNA- Como Convertir Una Imagen a Escala de Grises | Ideas de un Conejo Says:
    January 18th, 2010 at 9:11 am  

    [...] Con Bitmap y Picturebox =http://juank.black-byte.com/c-bitmap-convertir-imagen-escala-grises/ Con XNA y Shaders= http://juank.black-byte.com/xna-shaders-convertir-imagen-grises/ var addthis_pub = 'juankruiz'; var addthis_language = 'es';var addthis_options = 'facebook, [...]

  • C# - Bitmap - Como Convertir Una Imagen a Escala de Grises | Ideas de un Conejo Says:
    January 18th, 2010 at 9:13 am  

    [...] y Shaders: Con XNA y Texture 2D= http://juank.black-byte.com/xna-convertir-imagen-escala-grises/ Con XNA y Shaders= http://juank.black-byte.com/xna-shaders-convertir-imagen-grises/ var addthis_pub = 'juankruiz'; var addthis_language = 'es';var addthis_options = 'facebook, [...]

Deja un comentario

Redes Sociales

Follow @JuanKRuiz
Answer Questions

Busca en el blog

Artículos Relacionados

  • C# – XNA- Como Convertir Una Imagen a Escala de Grises
  • C# – Bitmap – Como Convertir Una Imagen a Escala de Grises
  • Optimización de Código – Cómo Convertir un Entero en Binario – C#
  • Detección de Colisiones 2D básica
  • El XNA Content Pipeline
  • Como abrir la puerta del cd rom desde C# y VB.NET
  • Como cambiar el texto de los botones de un MessageBox – C#
  • Artículos Relacionados

  • C# – XNA- Como Convertir Una Imagen a Escala de Grises
  • C# – Bitmap – Como Convertir Una Imagen a Escala de Grises
  • Optimización de Código – Cómo Convertir un Entero en Binario – C#
  • Detección de Colisiones 2D básica
  • El XNA Content Pipeline
  • Como abrir la puerta del cd rom desde C# y VB.NET
  • Como cambiar el texto de los botones de un MessageBox – C#
  • Nube de Temas

    API - Azure - C# - codigo - Forms - IE - IE9 - Image - imagenes - IT - Microsoft - MVP - Pinned - PowerShell - Proceso - rendimiento - RSS - sistema - Sistemas Operativos - Site - Visual - WCF - Windows - Windows 8 - Windows Store - WinRT - WndProc - WPF - XAML - XNA

    Blogs recomendados

  • VBCodigoPocketPC Espacio para tratar temas de programacion para dispositivos moviles, Pocket PC, Compact Framework, Embbeded Visual Basic, Visual Basic.NET , C# (C Sharp)
  • Róbinson Moscoso Estaré publicando acá cosas sobre tecnologia .NET, situacioines cotidianas de las que voy aprendiendo… sirve como extensión de memoria.
  • .Net C# Blog de Nelsón Venegas
  • Warnov Microsoft Developer Evangelist
  • IT LIfe Blog de mi Hermano que esta en el lado claro: IT
  • Sorey Garcia Una chica del común con la firme intención de no serlo
  • Black Byte videojuegos, modelado y animación 3d
  • Road to IT World Cosas interesantes de IT
  • Marcela Chitiva Un poco de esto… un poco de aquello
  • Surviving the Nigth El mejor blog para aquellos que nos gustan los “internals”
  • Meta

    1. Log in
    2. WordPress

    Ideas de un Conejo is powered by Wordpress. Theme designed by Juan Carlos Ruiz.