Забавы с пиксельным шейдером. Инверсия, Монохромное изображение, Блочность.

В этот раз мы рассмотрим еще несколько интересных пиксельных шейдера, которые могут вам когда-нибудь пригодиться.

Начнем с очень простого, но эффектного, шейдера – шейдера инверсии цветов.

Код шейдера элементарен: нужно просто вычесть из 1 значение по каждому каналу.

float4 PSInverse(float2 TexCoords : TEXCOORD0) : COLOR0
{
float4 color1 = tex2D(ColorSampler, TexCoords);

color1.r = 1 — color1.r;
color1.g = 1 — color1.g;
color1.b = 1 — color1.b;

return color1;
}

technique Inverse

{

    pass Pass1

    {

        PixelShader = compile ps_2_0 PSInverse();

    }

}

Получится следующий результат:

Следующий шейдер будет более интересным. Он будет визуально делить изображение на квадратные блоки. Такой шейдер может быть полезен при создании ретро игр.

 
float4 PSBlocks(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float kx = 50;
          float ky = 50;
          TexCoords.x = round(TexCoords.x * kx) / kx;
          TexCoords.y = round(TexCoords.y * ky) / ky;
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          return color1;
}

technique Blocks
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSBlocks();
    }
}

Размер блоков зависит от коэффициентов kx и ky.

Следующие несколько шейдеров нужны для создания различных видео однотонных изображений.

Первый просто будет вычислять среднее значение по всем трем цветовым каналам и устанавливать это значение во все цветовые каналы:

 
float4 PSMonochrome(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float c = (color1.r + color1.g + color1.b) / 3.0f;
 
          color1.r = c;
          color1.g = c;
          color1.b = c;
 
          return color1;
}
technique Monochrome
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSMonochrome();
    }
}

Следующий шейдер основывается на шейдере Monochrome и создает картинку, которая будет содержать только черный и белый цвета:

 
float4 PSBlackAndWhite(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float c = (color1.r + color1.g + color1.b) / 3.0f;
 
          if (c < 0.5)
                    c = 0.0f;
          else
                    c = 1.0f;
 
          color1.r = c;
          color1.g = c;
          color1.b = c;
 
          return color1;
}
technique BlackAndWhite
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSBlackAndWhite();
    }
}

Следующий шейдер будет также создавать однотонное изображение, но уже основываясь на яркости цвета. Как мы знаем, яркость определяется по следующей формуле:

Y = 0.299 * R + 0.587 * G + 0.114 * B;

Коэффициенты в формуле связаны с разницей восприятия компонентов цвета человеческим глазом.

 
float4 PSIntensity(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float3 luminance = float3(0.299, 0.587, 0.114);
          float4 color1 = tex2D(ColorSampler, TexCoords);
          float intensity = dot(color1, luminance);
 
          color1.r = intensity;
          color1.g = intensity;
          color1.b = intensity;
          
          return color1;
}
 
technique Intensity
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSIntensity();
    }
}

 


На основе этого шейдера позже мы сделаем еще несколько интересных эффектов.

Весь исходный код проекта:

Game1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
 
namespace PixelShaderFun
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
 
        Texture2D background;
        Texture2D background2;
        Texture2D background3;
        Effect effect;
 
        int currentTehnique;
        KeyboardState oldKs;
 
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
 
            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 480;
        }
 
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
 
            base.Initialize();
        }
 
        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
 
            // TODO: use this.Content to load your game content here
            background = Content.Load<Texture2D>("tulips");
            background2 = Content.Load<Texture2D>("koala");
            background3 = Content.Load<Texture2D>("flowers");
 
            effect = Content.Load<Effect>("pixelEffect");
        }
 
        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
 
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
 
            // TODO: Add your update logic here
            KeyboardState ks = Keyboard.GetState();
            if (ks.IsKeyDown(Keys.Space) && oldKs.IsKeyUp(Keys.Space))
            {
                currentTehnique = (currentTehnique + 1) % effect.Techniques.Count;
                effect.CurrentTechnique = effect.Techniques[currentTehnique];
            }
 
            if (ks.IsKeyDown(Keys.Back) && oldKs.IsKeyUp(Keys.Back))
            {
                currentTehnique = (currentTehnique - 1);
                if (currentTehnique < 0) currentTehnique = effect.Techniques.Count - 1;
                effect.CurrentTechnique = effect.Techniques[currentTehnique];
            }
 
            oldKs = ks;
            Window.Title = effect.CurrentTechnique.Name;
            base.Update(gameTime);
        }
 
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
 
            // TODO: Add your drawing code here            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, effect);
            spriteBatch.Draw(background3, new Rectangle(0, 0, 640, 480), Color.White);
            spriteBatch.End();
 
            base.Draw(gameTime);
        }
    }
}

 

pixelEffect.fx

sampler  ColorSampler  : register(s0);


float4 donothing(float2 TexCoords : TEXCOORD0) : COLOR0
{
          return tex2D(ColorSampler, TexCoords);
}
 
float4 PSRed(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.g = 0;
          color1.b = 0;
 
          return color1;
}
 
float4 PSGreen(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.r = 0;
          color1.b = 0;
 
          return color1;
}
 
float4 PSBlue(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.g = 0;
          color1.r = 0;
 
          return color1;
}
 
 
technique DoNothing
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 donothing();
    }
}
 
technique Red
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSRed();
    }
}
technique Green
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSGreen();
    }
}
technique Blue
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSBlue();
    }
}
 
 
float4 PSNoRed(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.r = 0;
 
          return color1;
}
 
float4 PSNoGreen(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.g = 0;
 
          return color1;
}
 
float4 PSNoBlue(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.b = 0;
 
          return color1;
}
 
technique NoRed
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSNoRed();
    }
}
technique NoGreen
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSNoGreen();
    }
}
technique NoBlue
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSNoBlue();
    }
}
 
 
float4 PSCyan(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float computedC = 0;
          float computedM = 0;
          float computedY = 0;
          float computedK = 0;
 
           if (color1.r==0 && color1.g==0 && color1.b==0) {
                    computedK = 1;
                    return float4(0,0,0,1);
           }
 
          computedC = 1 - (color1.r);
          computedM = 1 - (color1.g);
          computedY = 1 - (color1.b);
 
           float minCMY = min(computedC,
              min(computedM,computedY));
 computedC = (computedC - minCMY) / (1 - minCMY) ;
 computedM = (computedM - minCMY) / (1 - minCMY) ;
 computedY = (computedY - minCMY) / (1 - minCMY) ;
 computedK = minCMY;
 
          color1.r = 0;
          color1.g = computedC-computedK;
          color1.b = computedC-computedK;
 
          return color1;
}
 
float4 PSMagenta(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float computedC = 0;
          float computedM = 0;
          float computedY = 0;
          float computedK = 0;
 
           if (color1.r==0 && color1.g==0 && color1.b==0) {
                    computedK = 1;
                    return float4(0,0,0,1);
           }
 
          computedC = 1 - (color1.r);
          computedM = 1 - (color1.g);
          computedY = 1 - (color1.b);
 
          
          
           float minCMY = min(computedC,
              min(computedM,computedY));
 computedC = (computedC - minCMY) / (1 - minCMY) ;
 computedM = (computedM - minCMY) / (1 - minCMY) ;
 computedY = (computedY - minCMY) / (1 - minCMY) ;
 computedK = minCMY;
 
          color1.r = computedM-computedK;
          color1.g = 0;
          color1.b = computedM-computedK;
 
          return color1;
}
 
float4 PSYellow(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float computedC = 0;
          float computedM = 0;
          float computedY = 0;
          float computedK = 0;
 
           if (color1.r==0 && color1.g==0 && color1.b==0) {
                    computedK = 1;
                    return float4(0,0,0,1);
           }
 
 
          computedC = 1 - (color1.r);
          computedM = 1 - (color1.g);
          computedY = 1 - (color1.b);
 
          
           float minCMY = min(computedC,
              min(computedM,computedY));
 computedC = (computedC - minCMY) / (1 - minCMY) ;
 computedM = (computedM - minCMY) / (1 - minCMY) ;
 computedY = (computedY - minCMY) / (1 - minCMY) ;
 computedK = minCMY;
 
          color1.r = computedY - computedK;
          color1.g = computedY - computedK;
          color1.b = 0;
 
          return color1;
}
 
float4 PSBlack(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float computedC = 0;
          float computedM = 0;
          float computedY = 0;
          float computedK = 0;
 
           if (color1.r==0 && color1.g==0 && color1.b==0) {
                    computedK = 1;
                    return float4(0,0,0,1);
           }
 
 
          computedC = 1 - (color1.r);
          computedM = 1 - (color1.g);
          computedY = 1 - (color1.b);
 
          
           float minCMY = min(computedC,
              min(computedM,computedY));
 computedC = (computedC - minCMY) / (1 - minCMY) ;
 computedM = (computedM - minCMY) / (1 - minCMY) ;
 computedY = (computedY - minCMY) / (1 - minCMY) ;
 computedK = minCMY;
 
          color1.r = 1 - computedK;
          color1.g = 1 - computedK;
          color1.b = 1 - computedK;
 
          return color1;
}
 
 
technique Cyan
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSCyan();
    }
}
 
technique Magenta
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSMagenta();
    }
}
 
technique Yellow
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSYellow();
    }
}
 
technique Black
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSBlack();
    }
}
 
 
float4 PSInverse(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          color1.r = 1 - color1.r;
          color1.g = 1 - color1.g;
          color1.b = 1 - color1.b;
 
          return color1;
}
 
float4 PSBlocks(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float kx = 50;
          float ky = 50;
          TexCoords.x = round(TexCoords.x * kx) / kx;
          TexCoords.y = round(TexCoords.y * ky) / ky;
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          return color1;
}
 
 
technique Inverse
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSInverse();
    }
}
 
technique Blocks
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSBlocks();
    }
}
 
float4 PSBlackAndWhite(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float c = (color1.r + color1.g + color1.b) / 3.0f;
 
          if (c < 0.5)
                    c = 0.0f;
          else
                    c = 1.0f;
 
          color1.r = c;
          color1.g = c;
          color1.b = c;
 
          return color1;
}
technique BlackAndWhite
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSBlackAndWhite();
    }
}
 
float4 PSMonochrome(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float4 color1 = tex2D(ColorSampler, TexCoords);
 
          float c = (color1.r + color1.g + color1.b) / 3.0f;
 
          color1.r = c;
          color1.g = c;
          color1.b = c;
 
          return color1;
}
technique Monochrome
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSMonochrome();
    }
}
 
float4 PSIntensity(float2 TexCoords : TEXCOORD0) : COLOR0
{
          float3 luminance = float3(0.299, 0.587, 0.114);
          float4 color1 = tex2D(ColorSampler, TexCoords);
          float intensity = dot(color1, luminance);
 
          color1.r = intensity;
          color1.g = intensity;
          color1.b = intensity;
          
          return color1;
}
 
technique Intensity
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PSIntensity();
    }
}

 


					
Реклама
Запись опубликована в рубрике Компьютерная графика с метками . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s