Light Rendering Results in Low FPS - c#

I am currently developing a class for my XNA game whose rendering the lights on the image. At the time, i have made the source to draw my lightmap, however, the FPS is very low in my source. I know that it is brutally reduced upon looping through each pixel, however, I do not know any other way to get & set each pixel on my Texture in XNA but using the "For" statement?
Current Source:
public struct Light
{
public int Range;
public int Intensity;
public Color LightColor;
public Vector2 LightLocation;
public Light(int _Range, int _Intensity, Color _LightColor, Vector2 _LightLocation)
{
Range = _Range;
Intensity = _Intensity;
LightLocation = _LightLocation;
LightColor = _LightColor;
}
}
public class RenderClass
{
[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern bool MessageBox(IntPtr h, string S, string C, int a);
public static Texture2D RenderImage(Light[] LightLocations, Texture2D ScreenImage, Viewport v, bool ShadowBack = false)
{
Texture2D[] Images = new Texture2D[LightLocations.Count()];
int curCount = 0;
/*LOOP THROUGHT EACH LIGHT*/
foreach (Light LightLocation in LightLocations)
{
/*VARIABLES*/
Color LightColor = LightLocation.LightColor;
int Range = LightLocation.Range;
int Intensity = LightLocation.Intensity;
/*GET COLORS*/
int Width = v.Width;
int Height = v.Height;
Color[] Data = new Color[Width * Height];
ScreenImage.GetData<Color>(Data);
/*VARIABLES TO SET COLOR*/
Color[] SetColorData = new Color[Width * Height];
/*CIRCEL*/
int Radius = 15 / 2; // Define range to middle [Radius]
int Area = (int)Math.PI * (Radius * Radius);
for (int X = 0; X < Width; X++)
{
for (int Y = 0; Y < Height; Y++)
{
int Destination = X + Y * Width;
#region Light
/*GET COLOR*/
Color nColor = Data[Destination];
/*CREATE NEW COLOR*/
Vector2 MiddlePos = new Vector2(LightLocation.LightLocation.X + Radius, LightLocation.LightLocation.Y + Radius);
Vector2 CurrentLocation = new Vector2(X, Y);
float Distance;
Distance = Vector2.Distance(MiddlePos, CurrentLocation);
Distance *= 100;
Distance /= MathHelper.Clamp(Range, 0, 100);
Vector3 newColors = nColor.ToVector3();
nColor = new Color(
newColors.X,
newColors.Y,
newColors.Z,
Distance / 100);
/*SET COLOR*/
SetColorData[Destination] = nColor; // Add to array
#endregion
#region Shadow
#endregion
}
}
ScreenImage.SetData<Color>(SetColorData);
Images[curCount] = ScreenImage;
curCount++;
}
return Images[0]; // Temporarily returning the first image of the array.
}
}
As you can see, this is a slow and bad method. So I was wondering, is there a better way to get & set each pixel?
Thanks in advance, dotTutorials! =)

I think that job would be best done in a pixel shader.
You could create an Effect file that operates over one light at a time.XNA uses DX9 so you'll be limited to 128 constant registers, which I think you can use to squeeze up to three lights.
So you set your lightmap as a render target, loop through all the lights, set the constant data on your effect, render a render-target-sized quad and in your pixel shader compute your lighting equation.
In essence something like that:
// In LoadContent
RenderTarget2D lightmapRT = new RenderTarget2D(graphics.GraphicsDevice,
128,
128,
false, //No mip-mapping
SurfaceFormat.Color,
DepthFormat.Depth24);
// We now render to the lightmap in Render method
graphics.GraphicsDevice.SetRenderTarget(lightmapRT);
// Lightmap is black by default
graphics.GraphicsDevice.Clear(Color.Black);
// Use the sprite batch to draw quads with custom shader
spriteBatch.Begin(0, BlendState.Opaque, null, null, null, lightmapFx);
foreach (var light in lights)
{
// Pass the light parameters to the shader
lightmapFx.Parameters["Viewport"].SetValue(new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height));
lightmapFx.Parameters["Range"].SetValue(light.Range);
lightmapFx.Parameters["Intensity"].SetValue(light.Intensity);
lightmapFx.Parameters["LightColor"].SetValue(light.LightColor);
lightmapFx.Parameters["LightLocation"].SetValue(light.LightLocation);
// Render quad
spriteBatch.Draw(...);
}
spriteBatch.End();
And the FX file would look something like that:
float Range;
float Intensity;
float3 LightColor;
float2 LightLocation;
float2 Viewport;
struct VStoPS
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
VStoPS VS(in float4 color : COLOR0,
in float2 texCoord : TEXCOORD0,
in float4 position : POSITION0)
{
VStoPS vsout = (VStoPS)0;
// Half pixel offset for correct texel centering.
vsout.Position.xy -= 0.5;
// Viewport adjustment.
vsout.Position.xy = position.xy / Viewport;
vsout.Position.xy *= float2(2, -2);
vsout.Position.xy -= float2(1, -1);
// Pass texcoords as is
vsout.TexCoord = texCoord;
return vsout;
}
float4 PS(VStoPS psin)
{
// Do calculations here
// Here I just set it to white
return float4(1.0f, 1.0f, 1.0f, 1.0f);
}
technique Main
{
pass p0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}
Note that this non-tested code and probably full of errors. I leave it up to you to figure out what needs to go in the pixel shader.

Related

How to modify perlin noise to generate a simple shape?

I am working on a project in Unity that needs to generate some simple 3D Mountains/Hills. Since my requirements are to create a "simple" shape, I don't seem to find an answer and I thought maybe I can get some help from here. Anyways, this is a normal output from perlin noise, although it's smooth, the output is still complicated with lots of hills/mountains. I am looking for something like this . I need to be sure that I won't have any height around the borders of the Output Image. I think you've got the idea. Have a Great Day!
Here is the code I am using right now from an online tutorial:
using UnityEngine;
public class PerlinNoise : MonoBehaviour
{
private static int width = 256;
private static int height = 128;
public float scale = 20f;
public float offsetX = 100f;
public float offsetY = 100f;
private int xcont = 0, ycont = 0;
public float[,] array = new float[width,height];
private void Start()
{
offsetX = Random.Range(0f, 99999f);
offsetY = Random.Range(0f, 99999f);
}
void Update()
{
Renderer renderer = GetComponent<Renderer>();
renderer.material.mainTexture = GenerateTexture();
}
Texture2D GenerateTexture()
{
Texture2D texture = new Texture2D(width, height);
//GENERATE A PERLIN NOISE MAP FOR THE TEXTURE
for(int x=0;x<width;x++)
{
for(int y=0;y<height;y++)
{
Color color = CalculateColor(x,y);
texture.SetPixel(x, y, color);
}
}
texture.Apply();
return texture;
}
Color CalculateColor(int x, int y)
{
float xCoord = (float)x / width * scale + offsetX;
float yCoord = (float)y / height * scale + offsetY;
float sample = Mathf.PerlinNoise(xCoord,yCoord);
if (xcont == width - 1)
{
xcont = 0;
ycont++;
}
else xcont++;
if (ycont == height - 1 ) ycont = 0;
array[xcont,ycont] = sample;
return new Color(sample, sample, sample);
}
}
You can filter that texture down using cellular automata.
This playlist might help you understand how and when Perlin Noise is used for map generation: https://www.youtube.com/playlist?list=PLFt_AvWsXl0eZgMK_DT5_biRkWXftAOf9

How to apply textures to a material based on camera viewport in Unity

So this post is more or less a continuation of my last question here, so check that link if you're lost.
Basically, I need to load higher quality images applied to a material on a globe as the user zooms in the camera further. As it stands right now, I load all images at once depending on the zoom level, which is profoundly unoptimized at lower zoom levels, and impossible at higher ones since a texture2darray can only hold 2048 images.
What I need to know is, how could I only load the images that the camera is viewing, and not worry about ones off screen?
This may be a ludicrously specific problem, so I understand if I'm barking up the wrong tree here, but I figured I'd throw it out there and see if anyone has any insight.
Here's my current tiling class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TilerHelper : MonoBehaviour
{
MapTiler tiler = new MapTiler("G:/s2cloudless_4326_v1.0.0.sqlite"); //Function that loads from the sql table
public Material mat; //The material
Texture2DArray texArr; //The array that will be made into the final texture
public Texture2D[] textures; //The images loaded from the sql table
int size = 2048; //A texture2darray can only hold 2048 textures
int count = 0; //Iterator for loading images
int zoom = 1; //Determines which zoom level to load from the sql table
int lastZoom = 1; //Determines whether the zoom level has moved
CameraOrbit cam; //The main camera
int initX = 3; //The x value of the image at zoom level 1
int initY = 1;//The y value of the image at zoom level 1
// Start is called before the first frame update
void Start()
{
SetTiles(3, 1, 1, 4, 2); //This loads the first set of images at max distance when the program starts
cam = Camera.main.gameObject.GetComponent<CameraOrbit>();
}
// Update is called once per frame
void Update()
{
//These different zoom levels are based on the camera distance from the earth (large earth, so large numbers)
if (cam.distance <= 90000) zoom = 12;
else if (cam.distance <= 100000) zoom = 11;
else if (cam.distance <= 110000) zoom = 10;
else if (cam.distance <= 130000) zoom = 9;
else if (cam.distance <= 150000) zoom = 8;
else if (cam.distance <= 170000) zoom = 7;
else if (cam.distance <= 190000) zoom = 6;
else if (cam.distance <= 210000) zoom = 5;
else if (cam.distance <= 230000) zoom = 4;
else if (cam.distance <= 250000) zoom = 3;
else if (cam.distance <= 270000) zoom = 2;
else if (cam.distance >= 270000) zoom = 1;
//If the camera has gone to a new zoom level...
if(lastZoom != zoom)
{
if (zoom == 1) SetTiles(initX, initY, zoom, 4, 2); //Set it to 1 manually, since the formula won't work for it
else
{
//This formula will load the correct images in the correct places regardless of zoom level
int counter = 1;
int resultX = initX;
int resultY = initY;
while(counter < zoom)
{
resultX = resultX * 2 + 1;
resultY = resultY * 2 + 1;
counter++;
}
SetTiles(resultX, resultY, zoom, (int)Mathf.Pow(2, zoom + 1), (int)Mathf.Pow(2, zoom));
}
lastZoom = zoom; //Update last zoom
}
}
//The method that actually places the images
void SetTiles(int x, int y, int z, int columns, int rows)
{
textures = new Texture2D[size]; //The array to hold all the textures
//Load and place all the images according to passed x, y, and zoom level
for (int i = 0; i <= x; i++)
{
for (int j = 0; j <= y; j++)
{
textures[count] = tiler.Read(i, j, z); //The z determines the zoom level, so I wouldn't want them all loaded at once
count++;
}
}
count = 0; //Reset the counter
//Instantiate the texture2darray
texArr = new Texture2DArray(256, 256, textures.Length, TextureFormat.RGBA32, false, true);
texArr.filterMode = FilterMode.Bilinear;
texArr.wrapMode = TextureWrapMode.Clamp;
//Set the texture2darray to contain all images loaded
for (int i = 0; i < textures.Length; i++)
{
if (textures[i] == null) continue;
texArr.SetPixels(textures[i].GetPixels(), i, 0);
}
//Apply the texture and set appropriate material values
texArr.Apply();
mat.SetTexture("_MainTexArray", texArr);
Matrix4x4 matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, -90, 0), Vector3.one);
mat.SetMatrix("_Matrix", matrix);
mat.SetInt("_COLUMNS", columns);
mat.SetInt("_ROWS", rows);
}
}
And here is my shader code
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/Tiling"
{
Properties
{
_MainTexArray("Tex", 2DArray) = "" {}
_COLUMNS("Columns", Int) = 8
_ROWS("Rows", Int) = 4
}
SubShader
{
Pass{
Tags {"RenderType" = "Opaque"}
Lighting Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma require 2darray
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 normal : TEXCOORD0;
};
float4x4 _Matrix;
v2f vert(appdata v, float3 normal : TEXCOORD0)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = mul(v.normal, _Matrix);
return o;
}
UNITY_DECLARE_TEX2DARRAY(_MainTexArray);
int _ROWS;
int _COLUMNS;
#define PI 3.141592653589793
inline float2 RadialCoords(float3 a_coords)
{
float3 a_coords_n = normalize(a_coords);
float lon = atan2(a_coords_n.z, a_coords_n.x);
float lat = acos(a_coords_n.y);
float2 sphereCoords = float2(lon, lat) * (1.0 / PI);
return float2(sphereCoords.x * 0.5 + 0.5, 1 - sphereCoords.y);
}
float _UVClamp;
float4 frag(v2f IN) : COLOR
{
float2 equiUV = RadialCoords(IN.normal);
float2 texIndex;
float2 uvInTex = modf(equiUV * float2(_COLUMNS, _ROWS), texIndex);
int flatTexIndex = texIndex.x * _ROWS + texIndex.y;
return UNITY_SAMPLE_TEX2DARRAY(_MainTexArray,
float3(uvInTex, flatTexIndex));
}
ENDCG
}
}
}
Thank you.
Interesting problem to try and solve without looping through a lot of tiles or pixels. Here's a partial answer that might help you find a solution.
There are two main problem here:
Finding which "tiles" are visible to the camera.
Loading those tiles into the shader and having it index them correctly.
Finding which "tiles" are visible to the camera
The best idea I can come up here is to try and find a way to project the camera's view of the sphere onto where the visible points are on an equirectangular projection.
You could sample for this with multiple ViewportPointToRay and collision checks, but it would be inaccurate and especially in the cases where the camera can see "around" the sphere and the rays don't collide with the sphere there.
There may be a formulaic approach to this but I'm not sure what to do.
Loading those tiles into the shader and having it index them correctly.
This is the much easier part if you can determine which equirectangular tiles need to be sent. If you can draw a rectangle that loops around horizontally around all the points that the camera can see, then you can just send the tiles that are partially or completely inside that "loading rectangle".
You'll also need to send the start & endpoints of the rectangle. So the shader can calculate how to change the entire-map texIndex coordinates to where that tile is stored in the "loading rectangle".
Basically these lines in the shader would probably be the same:
float2 equiUV = RadialCoords(IN.normal);
float2 texIndex;
float2 uvInTex = modf(equiUV * float2(_COLUMNS, _ROWS), texIndex);
...
return UNITY_SAMPLE_TEX2DARRAY(_MainTexArray,
float3(uvInTex, flatTexIndex));
This line would have to change in some way to also take into account the loading rectangle parameters and how it horizontally wraps around the tile map:
int flatTexIndex = texIndex.x * _ROWS + texIndex.y;
Sorry about the vagueness, especially for the first part. Good luck.

Implementing Fur with Shells technique in Unity

I am trying to implement fur in Unity with the Shells technique. The Fins technique is purposely left out because I want this to run on low end mobiles (mostly Android devices) and that requires OpenGL ES 3.0 and above while Shells technique only requires OpenGL ES 2.0.
There is an example on the Shell technique based on XNA and I made an attempt to port that into Unity but it failed to work. Here is the article with the XNA project.
The XNA shader:
float4x4 World;
float4x4 View;
float4x4 Projection;
float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length
texture FurTexture;
sampler FurSampler = sampler_state
{
Texture = (FurTexture);
MinFilter = Point;
MagFilter = Point;
MipFilter = Point;
AddressU = Wrap;
AddressV = Wrap;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
VertexShaderOutput FurVertexShader(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;
float4 worldPosition = mul(float4(pos,1), World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.TexCoord = input.TexCoord;
return output;
}
float4 FurPixelShader(VertexShaderOutput input) : COLOR0
{
return tex2D(FurSampler, input.TexCoord);
}
technique Fur
{
pass Pass1
{
AlphaBlendEnable = true;
SrcBlend = SRCALPHA;
DestBlend = INVSRCALPHA;
CullMode = None;
VertexShader = compile vs_2_0 FurVertexShader();
PixelShader = compile ps_2_0 FurPixelShader();
}
}
The XNA C# script that controls the shader:
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
//simple camera for use in the game
Camera camera;
//texture containing fur data
Texture2D furTexture;
//effect for fur shaders
Effect furEffect;
//number of layers of fur
int nrOfLayers = 60;
//total length of the hair
float maxHairLength = 2.0f;
//density of hair
float density = 0.2f;
Texture2D furColorTexture;
//movement vectors
Vector3 gravity = new Vector3(0, -1.0f, 0);
Vector3 forceDirection = Vector3.Zero;
//final displacement for hair
Vector3 displacement;
/// <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
camera = new Camera(this);
Components.Add(camera);
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);
//generate the geometry
GenerateGeometry();
//load the effect
furEffect = Content.Load<Effect>("FurEffect");
//create the texture
furTexture = new Texture2D(GraphicsDevice,
256, 256, 1,
TextureUsage.None,
SurfaceFormat.Color);
//fill the texture
FillFurTexture(furTexture, density);
furColorTexture = Content.Load<Texture2D>("bigtiger");
}
/// <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
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)
{
forceDirection.X = (float)Math.Sin(gameTime.TotalGameTime.TotalSeconds) * 0.5f;
displacement = gravity + forceDirection;
furEffect.Parameters["Displacement"].SetValue(displacement);
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
furEffect.Parameters["World"].SetValue(Matrix.CreateTranslation(0, -10, 0));
furEffect.Parameters["View"].SetValue(camera.View);
furEffect.Parameters["Projection"].SetValue(camera.Projection);
furEffect.Parameters["MaxHairLength"].SetValue(maxHairLength);
furEffect.Parameters["FurTexture"].SetValue(furTexture);
furEffect.Parameters["Texture"].SetValue(furColorTexture);
furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
furEffect.Parameters["CurrentLayer"].SetValue((float)i / nrOfLayers);
furEffect.CommitChanges();
furEffect.CurrentTechnique.Passes[0].Begin();
DrawGeometry();
furEffect.CurrentTechnique.Passes[0].End();
}
furEffect.End();
base.Draw(gameTime);
}
/// <summary>
/// This functions prepares a texture to be used for fur rendering
/// </summary>
/// <param name="furTexture">This will contain the final texture</param>
/// <param name="density">Hair density in [0..1] range </param>
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.Width;
int height = furTexture.Height;
int totalPixels = width * height;
//an array to hold our pixels
Color[] colors;
colors = new Color[totalPixels];
//random number generator
Random rand = new Random();
//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = Color.TransparentBlack;
//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);
//compute the number of strands that stop at each layer
int strandsPerLayer = nrStrands / nrOfLayers;
//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//compute max layer
int max_layer = i / strandsPerLayer;
//normalize into [0..1] range
float max_layer_n = (float)max_layer / (float)nrOfLayers;
//put color (which has an alpha value of 255, i.e. opaque)
//max_layer_n needs to be multiplied by 255 to achieve a color in [0..255] range
colors[x * width + y] = new Color((byte)(max_layer_n * 255), 0, 0, 255);
}
//set the pixels on the texture.
furTexture.SetData<Color>(colors);
}
VertexPositionNormalTexture[] vertices;
private void GenerateGeometry()
{
vertices = new VertexPositionNormalTexture[6];
vertices[0] = new VertexPositionNormalTexture(
new Vector3(-10, 0, 0),
-Vector3.UnitZ,
new Vector2(0, 0));
vertices[1] = new VertexPositionNormalTexture(
new Vector3(10, 20, 0),
-Vector3.UnitZ,
new Vector2(1, 1));
vertices[2] = new VertexPositionNormalTexture(
new Vector3(-10, 20, 0),
-Vector3.UnitZ,
new Vector2(0, 1));
vertices[3] = vertices[0];
vertices[4] = new VertexPositionNormalTexture(
new Vector3(10, 0, 0),
-Vector3.UnitZ,
new Vector2(1, 0));
vertices[5] = vertices[1];
}
private void DrawGeometry()
{
using (VertexDeclaration vdecl = new VertexDeclaration(
GraphicsDevice,
VertexPositionNormalTexture.VertexElements))
{
GraphicsDevice.VertexDeclaration = vdecl;
GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, 2);
}
}
}
I carefully ported the both the shader and the control script line by line to Unity.
The Ported Unity shader:
Shader "Programmer/Fur Shader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
//_TintColor("Tint Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 100
Blend SrcAlpha One
Blend DstAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
//#pragma multi_compile_fog
#include "UnityCG.cginc"
//In
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//Out
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
//Test variable/delete after
float4 _TintColor;
//The variables
float4x4 World;
float4x4 View;
float4x4 Projection;
float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length
VertexShaderOutput vert(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;
float4 worldPosition = mul(float4(pos, 1), World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.TexCoord = input.TexCoord;
return output;
}
float4 frag(VertexShaderOutput i) : COLOR0
{
return tex2D(_MainTex, i.TexCoord);
}
ENDCG
}
}
}
The ported Unity C# script that controls the shader:
public class Game1 : MonoBehaviour
{
public Material material;
public Vector3 pos = new Vector3(0f, 0.98f, -9.54f);
//simple camera for use in the game
private new Camera camera;
//texture containing fur data
public Texture2D furTexture;
//effect for fur shaders
//Effect furEffect;
//number of layers of fur
public int nrOfLayers = 40;
//total length of the hair
public float maxHairLength = 2.0f;
//density of hair
public float density = 0.2f;
//[Space(20)]
//public Vector3 dirWorldVal = new Vector3(0, -10, 0);
void Start()
{
Initialize();
GenerateGeometry();
}
public void Update()
{
Draw();
}
void Initialize()
{
//Initialize the camera
camera = Camera.main;
//create the texture
furTexture = new Texture2D(256, 256, TextureFormat.ARGB32, false);
furTexture.wrapModeU = TextureWrapMode.Repeat;
furTexture.wrapModeV = TextureWrapMode.Repeat;
furTexture.filterMode = FilterMode.Point;
//fill the texture
FillFurTexture(furTexture, density);
/*XNA's SurfaceFormat.Color is ARGB.
//https://gamedev.stackexchange.com/a/6442/98839*/
if (material.mainTexture != null)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
}
bool firstDraw = true;
protected void Draw()
{
camera.backgroundColor = CornflowerBlue();
Matrix4x4 worldValue = Matrix4x4.Translate(pos);
Matrix4x4 viewValue = camera.projectionMatrix;
// viewValue = camera.worldToCameraMatrix;
Matrix4x4 projectionValue = camera.projectionMatrix;
material.SetMatrix("World", worldValue);
material.SetMatrix("View", viewValue);
material.SetMatrix("Projection", projectionValue); //Causes object to disappear
material.SetFloat("MaxHairLength", maxHairLength);
if (firstDraw)
material.SetTexture("_MainTex", furTexture);
//furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
material.SetFloat("CurrentLayer", (float)i / nrOfLayers);
DrawGeometry();
}
if (firstDraw)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
if (firstDraw)
firstDraw = false;
}
void DrawGeometry()
{
Quaternion rotation = Quaternion.Euler(0, 180, 0);
Graphics.DrawMesh(verticesMesh, pos, rotation, material, 0, camera);
}
private VertexPositionNormalTexture[] verticesPText;
public Mesh verticesMesh;
private void GenerateGeometry()
{
verticesPText = new VertexPositionNormalTexture[6];
verticesPText[0] = new VertexPositionNormalTexture(new Vector3(-10, 0, 0),
-UnitZ(),
new Vector2(0, 0));
verticesPText[1] = new VertexPositionNormalTexture(new Vector3(10, 20, 0),
-UnitZ(),
new Vector2(1, 1));
verticesPText[2] = new VertexPositionNormalTexture(new Vector3(-10, 20, 0),
-UnitZ(),
new Vector2(0, 1));
verticesPText[3] = verticesPText[0];
verticesPText[4] = new VertexPositionNormalTexture(new Vector3(10, 0, 0),
-UnitZ(),
new Vector2(1, 0));
verticesPText[5] = verticesPText[1];
verticesMesh = VertexPositionNormalTextureToUnityMesh(verticesPText);
}
Mesh VertexPositionNormalTextureToUnityMesh(VertexPositionNormalTexture[] vpnt)
{
Vector3[] vertices = new Vector3[vpnt.Length];
Vector3[] normals = new Vector3[vpnt.Length];
Vector2[] uvs = new Vector2[vpnt.Length];
int[] triangles = new int[vpnt.Length];
//Copy variables to create a mesh
for (int i = 0; i < vpnt.Length; i++)
{
vertices[i] = vpnt[i].Position;
normals[i] = vpnt[i].Normal;
uvs[i] = vpnt[i].TextureCoordinate;
triangles[i] = i;
}
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uvs;
mesh.triangles = triangles;
return mesh;
}
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.width;
int height = furTexture.height;
int totalPixels = width * height;
//an array to hold our pixels
Color32[] colors = new Color32[totalPixels];
//random number generator
System.Random rand = new System.Random();
//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = TransparentBlack();
//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);
//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//put color (which has an alpha value of 255, i.e. opaque)
colors[x * width + y] = Gold();
}
//set the pixels on the texture.
furTexture.SetPixels32(colors);
// actually apply all SetPixels, don't recalculate mip levels
furTexture.Apply();
}
Color32 TransparentBlack()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_TransparentBlack
Color32 color = new Color32(0, 0, 0, 0);
return color;
}
Color32 Gold()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_Gold
Color32 color = new Color32(255, 215, 0, 255);
return color;
}
Color32 CornflowerBlue()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_CornflowerBlue
Color32 color = new Color32(100, 149, 237, 255);
return color;
}
public static Vector3 UnitZ()
{
return new Vector3(0f, 0f, 1f);
}
}
The ported VertexPositionNormalTexture struct for Unity
public struct VertexPositionNormalTexture
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 TextureCoordinate;
//public static readonly VertexDeclaration VertexDeclaration;
public VertexPositionNormalTexture(Vector3 position, Vector3 normal, Vector2 textureCoordinate)
{
this.Position = position;
this.Normal = normal;
this.TextureCoordinate = textureCoordinate;
}
public override int GetHashCode()
{
// TODO: FIc gethashcode
return 0;
}
public override string ToString()
{
return string.Format("{{Position:{0} Normal:{1} TextureCoordinate:{2}}}", new object[] { this.Position, this.Normal, this.TextureCoordinate });
}
public static bool operator ==(VertexPositionNormalTexture left, VertexPositionNormalTexture right)
{
return (((left.Position == right.Position) && (left.Normal == right.Normal)) && (left.TextureCoordinate == right.TextureCoordinate));
}
public static bool operator !=(VertexPositionNormalTexture left, VertexPositionNormalTexture right)
{
return !(left == right);
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != base.GetType())
{
return false;
}
return (this == ((VertexPositionNormalTexture)obj));
}
}
The ported Unity work is not working properly. No shells and the output image is flat.
This is the expected result in XNA (Works fine):
But this is what I see in Unity (no shells):
The final image supposed to look like the image below but I can't go on with the porting work since the basic implementation is not working properly in Unity.
My script public variable settings:
Why is the the ported Unity result flat? Did I miss anything?
EDIT:
Leo mentioned about possible backface problem because Unity uses the left-handed coordinate system while XNA uses the right-handed coordinate system.
I flipped the UnitZ() value and also tried reversing the mesh vertices but there was nothing on the screen. This is not likely the issue.
Unity is doing a batch optimization on the material. You can see this in the frame debugger. Each DrawGeometry call is using the same value for CurrentLayer. You need to use a propertyblock for each call to DrawMesh.
Setting a new material causes some flickering.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace foo {
public class FurBehavior : MonoBehaviour
{
public Material material;
public Vector3 pos = new Vector3(0f, 0.98f, -9.54f);
//simple camera for use in the game
private new Camera camera;
//texture containing fur data
public Texture2D furTexture;
//effect for fur shaders
//Effect furEffect;
//number of layers of fur
public int nrOfLayers = 40;
//total length of the hair
public float maxHairLength = 2.0f;
//density of hair
public float density = 0.2f;
//[Space(20)]
//public Vector3 dirWorldVal = new Vector3(0, -10, 0);
void Start()
{
this.transform.position = new Vector3(0f, 0.98f, -9.54f);
this.transform.rotation = Quaternion.Euler(0, 180, 0);
Initialize();
GenerateGeometry();
}
public void Update()
{
Draw();
}
void Initialize()
{
//Initialize the camera
camera = Camera.main;
//create the texture
furTexture = new Texture2D(256, 256, TextureFormat.ARGB32, false);
furTexture.wrapModeU = TextureWrapMode.Repeat;
furTexture.wrapModeV = TextureWrapMode.Repeat;
//furTexture.filterMode = FilterMode.Point;
//fill the texture
FillFurTexture(furTexture, density);
/*XNA's SurfaceFormat.Color is ARGB.
//https://gamedev.stackexchange.com/a/6442/98839*/
if (material.mainTexture != null)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
// material.mainTexture.filterMode = FilterMode.Point;
}
}
bool firstDraw = true;
protected void Draw()
{
var pos = this.transform.position;
camera.backgroundColor = CornflowerBlue();
Matrix4x4 worldValue = Matrix4x4.Translate(pos);
Matrix4x4 viewValue = camera.projectionMatrix;
// viewValue = camera.worldToCameraMatrix;
Matrix4x4 projectionValue = camera.projectionMatrix;
material.SetMatrix("World", worldValue);
material.SetMatrix("View", viewValue);
material.SetMatrix("Projection", projectionValue); //Causes object to disappear
material.SetFloat("MaxHairLength", maxHairLength);
//if (firstDraw)
material.SetTexture("_MainTex", furTexture);
//furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
var propertyBlock = new MaterialPropertyBlock();
var layer = (float)i / (float)nrOfLayers;
propertyBlock.SetFloat("CurrentLayer", layer);
propertyBlock.SetFloat("MaxHairLength", maxHairLength);
propertyBlock.SetColor("_TintColor", new Color(layer, layer, layer, layer));
DrawGeometry(propertyBlock);
}
if (firstDraw)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
if (firstDraw)
firstDraw = false;
}
void DrawGeometry(MaterialPropertyBlock props)
{
var rot = Quaternion.Euler(0, 180, 0);
Graphics.DrawMesh(verticesMesh, pos, rot, material, 0, camera, 0, props);
}
private VertexPositionNormalTexture[] verticesPText;
public Mesh verticesMesh;
private void GenerateGeometry()
{
var UnitZ = new Vector3(0, 0, 1);
var verticesPText = new VertexPositionNormalTexture[6];
verticesPText[5] = new VertexPositionNormalTexture(new Vector3(-10, 0, 0),
-UnitZ,
new Vector2(0, 0));
verticesPText[4] = new VertexPositionNormalTexture(new Vector3(10, 20, 0),
-UnitZ,
new Vector2(1, 1));
verticesPText[3] = new VertexPositionNormalTexture(new Vector3(-10, 20, 0),
-UnitZ,
new Vector2(0, 1));
verticesPText[2] = verticesPText[5];
verticesPText[1] = new VertexPositionNormalTexture(new Vector3(10, 0, 0),
-UnitZ,
new Vector2(1, 0));
verticesPText[0] = verticesPText[4];
}
Mesh VertexPositionNormalTextureToUnityMesh(VertexPositionNormalTexture[] vpnt)
{
Vector3[] vertices = new Vector3[vpnt.Length];
Vector3[] normals = new Vector3[vpnt.Length];
Vector2[] uvs = new Vector2[vpnt.Length];
int[] triangles = new int[vpnt.Length];
//Copy variables to create a mesh
for (int i = 0; i < vpnt.Length; i++)
{
vertices[i] = vpnt[i].Position;
normals[i] = vpnt[i].Normal;
uvs[i] = vpnt[i].TextureCoordinate;
triangles[i] = i;
}
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uvs;
mesh.MarkDynamic();
mesh.triangles = triangles;
mesh.UploadMeshData(false);
return mesh;
}
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.width;
int height = furTexture.height;
int totalPixels = width * height;
//an array to hold our pixels
Color32[] colors = new Color32[totalPixels];
//random number generator
System.Random rand = new System.Random();
//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = TransparentBlack();
//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);
//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//put color (which has an alpha value of 255, i.e. opaque)
// colors[x * width + y] = new Color32((byte)255, (byte)x, (byte)y, (byte)255);
colors[x * width + y] = Gold();
}
//set the pixels on the texture.
furTexture.SetPixels32(colors);
// actually apply all SetPixels, don't recalculate mip levels
furTexture.Apply();
}
Color32 TransparentBlack()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_TransparentBlack
Color32 color = new Color32(0, 0, 0, 0);
return color;
}
Color32 Gold()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_Gold
Color32 color = new Color32(255, 215, 0, 255);
return color;
}
Color32 CornflowerBlue()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_CornflowerBlue
Color32 color = new Color32(100, 149, 237, 255);
return color;
}
public static Vector3 UnitZ()
{
return new Vector3(0f, 0f, 1f);
}
}
}
I also modified the shader to visualize the shells.
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with
'UnityObjectToClipPos(*)'
Shader "Programmer/Fur Shader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_TintColor("Tint Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 100
//Blend SrcAlpha One
//Blend DstAlpha OneMinusSrcAlpha
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
//#pragma multi_compile_fog
#include "UnityCG.cginc"
//In
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//Out
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Tint: COLOR1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
//Test variable/delete after
float4 _TintColor;
//The variables
float4x4 World;
float4x4 View;
float4x4 Projection;
float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length
VertexShaderOutput vert(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;
//float4 worldPosition = mul(float4(pos, 1), World);
//float4 viewPosition = mul(worldPosition, View);
output.Position = UnityObjectToClipPos(pos);
output.TexCoord = input.TexCoord;
output.Tint = float4(CurrentLayer, CurrentLayer, 0, 1);
return output;
}
float4 frag(VertexShaderOutput i) : COLOR0
{
float4 t = tex2D(_MainTex, i.TexCoord) * i.Tint;
return t;//float4(t, i.x, i.y, 1);
}
ENDCG
}
}
}
Here's what it looks like after messing with the parameters and moving the camera a bit.

HLSL and XNA: How do I get the position and color of surrounding pixels of the current pixel shader iteration?

Abstract and Goal:
I am trying to make a shader to perform a simple window effect for a game editor. The effect will draw a frame with a low value border color and a high value highlight. I've tried many methods, but overall I have only come up with one possible solution to achieve this using the GPU.
First I create a custom vertex type for storing the XY coordinates of a vector in screen space.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace WindowsGame1 {
public struct Vertex2D : IVertexType {
public Vector2 Position;
public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0));
public Vertex2D(float x, float y) {
Position = new Vector2(x, y);
}
VertexDeclaration IVertexType.VertexDeclaration {
get {
return VertexDeclaration;
}
}
}
}
Next I create an instance of a custom Window class. The constructor sets up the vertex buffer and sets the view, projection, and color parameters in the effect.
Here is the effect file.
float4x4 view;
float4x4 projection;
float4 color;
float shadowPercent = 0.36893203883495145631067961165049;
float highlightPercent = 1.262135922330097087378640776699;
Texture2D targetTexture;
struct FillVertexShaderInput {
float4 position : POSITION0;
};
struct FillPixelShaderInput {
float4 position : POSITION0;
};
struct BorderPixelShaderInput {
float4 position : SV_Position;
};
// Transforms color component range from 0-255 to 0-1.
float4 ClampColor(float4 color) {
return float4(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255);
}
// Shifts the value of a color by a percent to get border color and highlight color from a fill color.
float4 ShiftValue(float4 color, float percent) {
return float4(clamp(color[0] * percent, 0, 1), clamp(color[1] * percent, 0, 1), clamp(color[2] * percent, 0, 1), clamp(color[3] * percent, 0, 1));
}
FillPixelShaderInput FillVertexShader(FillVertexShaderInput input) {
FillPixelShaderInput output;
output.position = mul(mul(input.position, view), projection);
return output;
}
float4 FillPixelShader(FillPixelShaderInput input) : COLOR0 {
return color;
}
float4 BorderPixelShader(BorderPixelShaderInput input) : COLOR0 {
// Get color of pixel above?
// float4 tempColor = texture.Sample(sampler, (input.position[0], input.position[1] - width));
return color;
}
technique Frame {
// Store Texture2D, sampler2D, and others to be stored between passes?
/*Texture2D texture;
sampler2D sampler;
float width;
float height;
texture.GetDimensions(width, height);
color = ClampColor(color);
float4 shadowColor = ShiftValue(color, shadowPercent);
float4 highlightColor = ShiftValue(color, highlightPercent);*/
pass Fill {
VertexShader = compile vs_2_0 FillVertexShader();
PixelShader = compile ps_2_0 FillPixelShader();
}
pass Border {
PixelShader = compile ps_4_0 BorderPixelShader();
}
}
I would like to be able to store the data between passes, but I don't know if that is possible so I tried storing a render target in XNA and using it as a parameter for the next pass.
Here is the Draw code of the Window.
public void Draw(Game1 game) {
// rectangle is a simple window for this test.
RenderTarget2D target = new RenderTarget2D(game.GraphicsDevice, rectangle.Width, rectangle.Height);
game.GraphicsDevice.SetRenderTarget(target);
game.GraphicsDevice.BlendState = BlendState.AlphaBlend;
game.GraphicsDevice.Clear(Color.Transparent);
game.GraphicsDevice.SetVertexBuffer(vertexbuffer);
effect.Techniques["Frame"].Passes["Fill"].Apply();
game.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
game.GraphicsDevice.SetRenderTarget(null);
effect.Parameters["targetTexture"].SetValue(target);
effect.Techniques["Frame"].Passes["Border"].Apply();
}
If I can get the position and color of surrounding pixels of the current pixel in a pixel shader, I can determine what color to draw the pixel at the current position. The Fill works fine. I just don't know the best way to go about drawing the border shadow and highlight. Also I am having problems with the alpha blending. Everything except the window is the default dark purple color, even though I set alpha blending and clear the render target buffer to transparent.
Thanks in advance if you decide to help.
I believe it works like this:
PixelShaderOutput PixelShaderFunction(VertexShaderOutput input, float2 vPos : VPOS)
{
// shader code here
}
Then you can just use vPos.x and vPos.y to access the coordinates of current pixel being processed.

How can I use a Shader in XNA to color single pixels?

I have a standard 800x600 window in my XNA project. My goal is to color each individual pixel based on a rectangle array which holds boolean values. Currently I am using a 1x1 Texture and drawing each sprite in my array.
I am very new to XNA and come from a GDI background, so I am doing what I would have done in GDI, but it doesn't scale very well. I have been told in another question to use a Shader, but after much research, I still haven't been able to find out how to accomplish this goal.
My application loops through the X and Y coordinates of my rectangular array, does calculations based on each value, and reassigns/moves the array around. At the end, I need to update my "Canvas" with the new values. A smaller sample of my array would look like:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
How can I use a shader to color each pixel?
A very simplified version of the calculations would be:
for (int y = _horizon; y >= 0; y--) // _horizon is my ending point
{
for (int x = _width; x >= 0; x--) // _width is obviously my x length.
{
if (grains[x, y] > 0)
{
if (grains[x, y + 1] == 0)
{
grains[x, y + 1] = grains[x, y];
grains[x, y] = 0;
}
}
}
}
..each time the update method is called, the calculations are performed and in example of the above loop, an update may look like:
Initial:
0,0,0,1,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
First:
0,0,0,0,0,0,0
0,0,0,1,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Second:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,1,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Final:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
Update:
After applying the Render2DTarget code and placing my pixels, I end up with an unwanted border on my pixels, always to the left. How can I remove this?
alt text http://www.refuctored.com/borders.png
alt text http://www.refuctored.com/fallingdirt.png
The some of the code for applying the textures is:
RenderTarget2D target;
Texture2D texture;
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("grain");
_width = this.Window.ClientBounds.Width - 1;
_height = this.Window.ClientBounds.Height - 1;
target = new RenderTarget2D(this.GraphicsDevice,_width, _height, 1, SurfaceFormat.Color,RenderTargetUsage.PreserveContents);
}
protected override void Draw(GameTime gameTime)
{
this.GraphicsDevice.SetRenderTarget(0, target);
this.GraphicsDevice.SetRenderTarget(0, null);
this.GraphicsDevice.Clear(Color.SkyBlue);
this.spriteBatch.Begin(SpriteBlendMode.None,SpriteSortMode.Deferred,SaveStateMode.None);
SetPixels(texture);
this.spriteBatch.End();
}
private void SetPixels(Texture2D texture)
{
for (int y = _grains.Height -1; y > 0; y--)
{
for (int x = _grains.Width-1; x > 0; x--)
{
if (_grains.GetGrain(x, y) >0)
{
this.spriteBatch.Draw(texture, new Vector2(x,y),null, _grains.GetGrainColor(x, y));
}
}
}
}
This method doesn't use pixel shaders, but if you're looking to use Texture2D's SetData method instead of making a call to SpriteBatch.Draw() for every pixel, you may find this useful. I used an array of uint instead of bool to represent your colors. If you can get away with an 8-bit color texture, you could may be able to speed this up by changing the texture format.
public class Game1 : Microsoft.Xna.Framework.Game
{
// Set width, height
const int WIDTH = 800;
const int HEIGHT = 600;
// Used to randomly fill in initial data, not necessary
Random rand;
// Graphics and spritebatch
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// Texture you will regenerate each call to update
Texture2D texture;
// Data array you perform calculations on
uint[] data;
// Colors are represented in the texture as 0xAARRGGBB where:
// AA = alpha
// RR = red
// GG = green
// BB = blue
// Set the first color to red
const uint COLOR0 = 0xFFFF0000;
// Set the second color to blue
const uint COLOR1 = 0xFF0000FF;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Set width, height
graphics.PreferredBackBufferWidth = WIDTH;
graphics.PreferredBackBufferHeight = HEIGHT;
}
protected override void Initialize()
{
base.Initialize();
// Seed random, initialize array with random picks of the 2 colors
rand = new Random((int)DateTime.Now.Ticks);
data = new uint[WIDTH * HEIGHT];
loadInitialData();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Create a new texture
texture = new Texture2D(GraphicsDevice, WIDTH, HEIGHT);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// Run-time error without this
// Complains you can't modify a texture that has been set on the device
GraphicsDevice.Textures[0] = null;
// Do the calculations
updateData();
// Update the texture for the next time it is drawn to the screen
texture.SetData(data);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
// Draw the texture once
spriteBatch.Begin();
spriteBatch.Draw(texture, Vector2.Zero, Color.Purple);
spriteBatch.End();
base.Draw(gameTime);
}
private void loadInitialData()
{
// Don't know where the initial data comes from
// Just populate the array with a random selection of the two colors
for (int i = 0; i < WIDTH; i++)
for (int j = 0; j < HEIGHT; j++)
data[i * HEIGHT + j] = rand.Next(2) == 0 ? COLOR0 : COLOR1;
}
private void updateData()
{
// Rough approximation of calculations
for(int y = HEIGHT - 1; y >= 0; y--)
for (int x = WIDTH - 1; x >= 0; x--)
if (data[x * HEIGHT + y] == COLOR1)
if (y + 1 < HEIGHT && data[x * HEIGHT + (y + 1)] == COLOR0)
{
data[x * HEIGHT + (y + 1)] = data[x * HEIGHT + y];
data[x * HEIGHT + y] = COLOR0;
}
}
}
How about this...
Create two textures (800x600)
Initialize one of them to the initial values.
For each frame you render one texture to the other while updating the values in a pixelshader.
After rendering the resulting texture to the screen, you swap them, so they are ready for your next frame.
Edit:
You will need two instances of RenderTarget2D and create them with RenderTargetUsage.PreserveContents. You could start with SurfaceFormat.Color and use black for 0 and white for 1. (You may also be able to find a 8 bit format to save video memory.)
new RenderTarget2D(_device, 800, 600, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);
You assign them to the rendertaget like this:
_device.SetRenderTarget(0, myRenderTarget);
You use a RenderTarget2D as a texture, like this:
_device.Textures[0] = myRenderTarget.GetTexture();
Hope that helps... I can dig out more from my engine, so just ask.
What you're trying to do (draw pixel by pixel) is what DirectX does. XNA is a layer that is built on top of DirectX so that you don't have to draw pixel by pixel. If that's really what you want to do then you should probably be learning DirectX instead of XNA. You would probably find it much easier...
Add a billboard to your scene (directly in front of the camera, so that it takes up exactly 800x600 pixels).
Bind the binary array as a texture.
In the fragment (/pixel) shader calculate the color of the fragment using the 2D position of the fragment and the texture.

Categories

Resources