I'm currently using the following to apply a texture to a polygon formed by TriangleList
public static VertexPositionColorTexture[] TextureMapping(VertexPositionColorTexture[] vertices, float xScale, float yScale)
{
bool initialized = false;
float x, y;
float lX = 0, hX = 0, lY = 0, hY = 0;
for (int i = 0; i < vertices.Length; i++)
{
x = vertices[i].Position.X;
y = vertices[i].Position.Y;
if (!initialized)
{
hX = x;
lX = x;
hX = y;
hY = y;
initialized = true;
}
else
{
if (x > hX)
{
hX = x;
}
else if (x < lX)
{
lX = x;
}
if (y > hY)
{
hY = y;
}
else if (y < lY)
{
lY = y;
}
}
}
float width = (Math.Abs(lX) + Math.Abs(hX)) / xScale;
float height = (Math.Abs(lY) + Math.Abs(hY)) / yScale;
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].TextureCoordinate.X = vertices[i].Position.X / width;
vertices[i].TextureCoordinate.Y = vertices[i].Position.Y / height;
}
return vertices;
This currently works fine for a polygon that has points that all have Z=0 (example: (0,0,0) (0,10,0) (10,10,0) (10,0,0)) but doesn't work for any that are rotated or not flat along the z (example (0,0,0) (0,0,10) (0,10,10) (0,10,0)). The only solution I have come with is to get the plane that the polygon lies on (it will always be flat) and somehow rotate or translate the vertices in the above method to flatten it to the xy line to allow for the correct height and width to be determined. Anyone point me in the right direction, or suggest something else?
Solved this myself by re-writing and rotating the polygon to the z plane.
Related
I am trying to generate a grid across my map and add nodes depending on the perlin noise value. Depending on the value obtained from the perlin noise at a location, I will add a new Node which will be of a certain type e.g. Mountain, Water etc to represent terrian. Here I am trying to make it so that if the value is > 0.5, this mean it's only mountains and so a black coloured cubes should surround the mountain areas, However, my black cubes do not match the mountain areas from the perlin noise and I cannot seem to figure out why I am going wrong. Would appreciate any insight into how I could go about achieving this.
private void LocateWalkableCells()
{
for(int z = 0; z < Height; z++)
{
for(int x = 0; x < Width; x++)
{
noise = GetNoiseValue(x, z);
if(noise > 0.5) {
grid[x,z] = new Node(new Vector3(x, 0, z), TerrainType.Mountain, 1);
}
else {
grid[x,z] = new Node(new Vector3(x, 0, z), TerrainType.Grass, 1);
}
}
}
}
private float GetNoiseValue(int x, int z)
{
int pos = (x * Width) + z;
return Mathf.Round(terrainGenerator.noiseArray[pos] * 10) / 10;
}
// Draw gizmos to visualize colour
void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(Width, 1, Height));
if(grid != null)
{
foreach(Node n in grid)
{
if(n.TerrainType == TerrainType.Grass)
{
Gizmos.color = Color.green;
}
else if(n.TerrainType == TerrainType.Mountain)
{
Gizmos.color = Color.black;
}
Gizmos.DrawCube(n.Position, Vector3.one * (nodeDiameter - .1f));
}
}
}
noiseArray is used for the vertices of the terrain in the following code:
vertices = new Vector3[(Width + 1) * (Depth + 1)];
noiseArray = PerlinNoise();
int i = 0;
for(int z = 0; z <= Depth; z++)
{
for(int x = 0; x <= Width; x++)
{
var currentHeight = noiseArray[i];
if(currentHeight > HeightThreshold)
{
currentHeight *= HeightMultiplier;
}
vertices[i] = new Vector3(x, currentHeight, z);
i++;
}
}
Output
Result from suggested answer
Still seems to miss some mountain areas, colouring green instead of black.
It think the issue is in
var pos = (x * Width) + z;
since x is you index on the width of the grid you would probably rather want
var pos = z * Width + x;
in other words you want to
skip z rows
each row has Width elements
then from there take the xth element
assuming your terrain is laid out row-wise.
Or if it is laid out column-wise (which is rather unusual but possible)
var pos = x * Height + z;
or in other words
skip x columns
each column has Height elements
then from there take the zth element
See also Converting index of one dimensional array into two dimensional array i. e. row and column
Update
Now that you have showed the terrain generation code it needs to be
var pos = z * (Width + 1) + x;
since the terrain array has actually Width + 1 elements per row.
I am trying to implement Hough Line Transform.
Input. I am using the following image as input. This single line is expected to produce only one intersection of sine waves in the output.
Desired behavior. my source code is expected to produce the following output as it was generated by the sample application of AForge framework.
Here, we can see:
the dimension of the output is identical to the input image.
the intersection of sine waves are seen at almost at the center.
the intersection pattern of waves is very small and simple.
Present behavior. My source code is producing the following output which is different than that of the output generated by AForge.
the intersection is not at the center.
the wave patterns are also different.
Why is my code producing a different output?
.
Source Code
I have written the following code myself. The following is a Minimal, Complete, and Verifiable source code.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int inWidth = image.GetLength(0);
int inHeight = image.GetLength(1);
int inWidthHalf = inWidth / 2;
int inHeightHalf = inHeight / 2;
int outWidth = (int)Math.Sqrt(inWidth * inWidth + inHeight * inHeight);
int outHeight = 180;
int outHeightHalf = outHeight / 2;
houghMap = new int[outWidth, outHeight];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < inHeight; y++) //|
{ //|
for (int x = 0; x < inWidth; x++)//<-----------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves. So, it may
// vary from -90 to +90 degrees.
for (int theta = -outHeightHalf; theta < outHeightHalf; theta++)
{
double rad = theta * Math.PI / 180;
// respective radius value is computed
//int radius = (int)Math.Round(Math.Cos(rad) * (x - inWidthHalf) - Math.Sin(rad) * (y - inHeightHalf));
//int radius = (int)Math.Round(Math.Cos(rad) * (x + inWidthHalf) - Math.Sin(rad) * (y + inHeightHalf));
int radius = (int)Math.Round(Math.Cos(rad) * (x) - Math.Sin(rad) * (outHeight - y));
// if the radious value is between 1 and
if ((radius > 0) && (radius <= outWidth))
{
houghMap[radius, theta + outHeightHalf]++;
}
}
}
}
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Bitmap bitmap = (Bitmap)pictureBox1.Image as Bitmap;
int[,] intImage = ToInteger(bitmap);
HoughMap houghMap = new HoughMap();
houghMap.image = intImage;
houghMap.Compute();
int[,] normalized = Rescale(houghMap.houghMap);
Bitmap hough = ToBitmap(normalized, bitmap.PixelFormat);
pictureBox2.Image = hough;
}
public static int[,] Rescale(int[,] image)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
int minVal = 0;
int maxVal = 0;
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
double conv = imageCopy[i, j];
minVal = (int)Math.Min(minVal, conv);
maxVal = (int)Math.Max(maxVal, conv);
}
}
int minRange = 0;
int maxRange = 255;
int[,] array2d = new int[Width, Height];
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
array2d[i, j] = (maxRange - minRange) * (imageCopy[i,j] - minVal) / (maxVal - minVal) + minRange;
}
}
return array2d;
}
public int[,] ToInteger(Bitmap input)
{
int Width = input.Width;
int Height = input.Height;
int[,] array2d = new int[Width, Height];
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Color cl = input.GetPixel(x, y);
int gray = (int)Convert.ChangeType(cl.R * 0.3 + cl.G * 0.59 + cl.B * 0.11, typeof(int));
array2d[x, y] = gray;
}
}
return array2d;
}
public Bitmap ToBitmap(int[,] image, PixelFormat pixelFormat)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
Bitmap bitmap = new Bitmap(Width, Height, pixelFormat);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
int iii = imageCopy[x, y];
Color clr = Color.FromArgb(iii, iii, iii);
bitmap.SetPixel(x, y, clr);
}
}
return bitmap;
}
}
I have solved the problem from this link. The source code from this link is the best one I have ever came across.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int Width = image.GetLength(0);
int Height = image.GetLength(1);
int centerX = Width / 2;
int centerY = Height / 2;
int maxTheta = 180;
int houghHeight = (int)(Math.Sqrt(2) * Math.Max(Width, Height)) / 2;
int doubleHeight = houghHeight * 2;
int houghHeightHalf = houghHeight / 2;
int houghWidthHalf = maxTheta / 2;
houghMap = new int[doubleHeight, maxTheta];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < Height; y++) //|
{ //|
for (int x = 0; x < Width; x++)//<-------------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves.
// It may vary from -90 to +90 degrees.
for (int theta = 0; theta < maxTheta; theta++)
{
double rad = theta *Math.PI / 180;
// respective radius value is computed
int rho = (int)(((x - centerX) * Math.Cos(rad)) + ((y - centerY) * Math.Sin(rad)));
// get rid of negative value
rho += houghHeight;
// if the radious value is between
// 1 and twice the houghHeight
if ((rho > 0) && (rho <= doubleHeight))
{
houghMap[rho, theta]++;
}
}
}
}
}
}
}
}
Just look at this C++ code, and this C# code. So, complicated and messy that my brain got arrested. Especially, the C++ one. I never anticipated someone to store 2D values in a 1D array.
I am learning C# for unity and could use some pointers.
I am following catlikecoding hex map tutorial but I have modified the grid for my own means.
http://catlikecoding.com/unity/tutorials/hex-map-1/
My goal is to create a pyramid of squares procedurally starting from a 7 * 7 grid. I am using a prefab plane
How do I place a limit on The CreateCell looped function so that cells with the (x,y) coordinates are not created when they meet the following expression
x + y > n - 1 where n = grid size (for example (6,1) or (5,6)
I have gotten as far as creating a rhombus of planes with the undesired planes below the ground plane.
The script is as follows.
public class HexGrid : MonoBehaviour {
public int width = 7;
public int height = 7;
public int length = 1;
public SquareCell cellPrefab;
public Text cellLabelPrefab;
SquareCell[] cells;
Canvas gridCanvas;
void Awake () {
gridCanvas = GetComponentInChildren<Canvas>();
cells = new SquareCell[height * width * length];
for (int z = 0 ; z < height; z++) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < length; y++)
CreateCell(x, z, y);
}
}
}
void CreateCell(int x, int z, int y) {
Vector3 position;
position.x = x * 10f ;
position.y = ((y + 1) - (x + z)) * 10f + 60f;
position.z = z * 10f ;
Cell cell = Instantiate<Cell>(cellPrefab);
cell.transform.SetParent(transform, false);
cell.transform.localPosition = position;
Text label = Instantiate<Text>(cellLabelPrefab);
label.rectTransform.SetParent(gridCanvas.transform, false);
label.rectTransform.anchoredPosition =
new Vector2(position.x, position.z);
label.text = x.ToString() + "\n" + z.ToString();
}
}
Grid so far
A quick solution would be to add an if statement before the part of the code that creates a cell. In this case the method CreateCell(). That if statement should have your logic in code. You would also have to create two variables for the size to check. For example:
public int tempX;
public int tempY;
void Awake () {
gridCanvas = GetComponentInChildren<Canvas>();
cells = new SquareCell[height * width * length];
for (int z = 0 ; z < height; z++) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < length; y++)
{
if (x + y < (tempX + tempY) - 1)
{
CreateCell(x, z, y);
}
}
}
}
}
I've been experimenting with the image bicubic resampling algorithm present in the AForge framework with the idea of introducing something similar into my image processing solution. See the original algorithm here and interpolation kernel here
Unfortunately I've hit a wall. It looks to me like somehow I am calculating the sample destination position incorrectly, probably due to the algorithm being designed for Format24bppRgb images where as I am using a Format32bppPArgb format.
Here's my code:
public Bitmap Resize(Bitmap source, int width, int height)
{
int sourceWidth = source.Width;
int sourceHeight = source.Height;
Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
destination.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using (FastBitmap sourceBitmap = new FastBitmap(source))
{
using (FastBitmap destinationBitmap = new FastBitmap(destination))
{
double heightFactor = sourceWidth / (double)width;
double widthFactor = sourceHeight / (double)height;
// Coordinates of source points
double ox, oy, dx, dy, k1, k2;
int ox1, oy1, ox2, oy2;
// Width and height decreased by 1
int maxHeight = height - 1;
int maxWidth = width - 1;
for (int y = 0; y < height; y++)
{
// Y coordinates
oy = (y * widthFactor) - 0.5;
oy1 = (int)oy;
dy = oy - oy1;
for (int x = 0; x < width; x++)
{
// X coordinates
ox = (x * heightFactor) - 0.5f;
ox1 = (int)ox;
dx = ox - ox1;
// Destination color components
double r = 0;
double g = 0;
double b = 0;
double a = 0;
for (int n = -1; n < 3; n++)
{
// Get Y cooefficient
k1 = Interpolation.BiCubicKernel(dy - n);
oy2 = oy1 + n;
if (oy2 < 0)
{
oy2 = 0;
}
if (oy2 > maxHeight)
{
oy2 = maxHeight;
}
for (int m = -1; m < 3; m++)
{
// Get X cooefficient
k2 = k1 * Interpolation.BiCubicKernel(m - dx);
ox2 = ox1 + m;
if (ox2 < 0)
{
ox2 = 0;
}
if (ox2 > maxWidth)
{
ox2 = maxWidth;
}
Color color = sourceBitmap.GetPixel(ox2, oy2);
r += k2 * color.R;
g += k2 * color.G;
b += k2 * color.B;
a += k2 * color.A;
}
}
destinationBitmap.SetPixel(
x,
y,
Color.FromArgb(a.ToByte(), r.ToByte(), g.ToByte(), b.ToByte()));
}
}
}
}
source.Dispose();
return destination;
}
And the kernel which should represent the given equation on Wikipedia
public static double BiCubicKernel(double x)
{
if (x < 0)
{
x = -x;
}
double bicubicCoef = 0;
if (x <= 1)
{
bicubicCoef = (1.5 * x - 2.5) * x * x + 1;
}
else if (x < 2)
{
bicubicCoef = ((-0.5 * x + 2.5) * x - 4) * x + 2;
}
return bicubicCoef;
}
Here's the original image at 500px x 667px.
And the image resized to 400px x 543px.
Visually it appears that the image is over reduced and then the same pixels are repeatedly applied once we hit a particular point.
Can anyone give me some pointers here to solve this?
Note FastBitmap is a wrapper for Bitmap that uses LockBits to manipulate pixels in memory. It works well with everything else I apply it to.
Edit
As per request here's the methods involved in ToByte
public static byte ToByte(this double value)
{
return Convert.ToByte(ImageMaths.Clamp(value, 0, 255));
}
public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0)
{
return min;
}
if (value.CompareTo(max) > 0)
{
return max;
}
return value;
}
You are limiting your ox2 and oy2 to destination image dimensions, instead of source dimensions.
Change this:
// Width and height decreased by 1
int maxHeight = height - 1;
int maxWidth = width - 1;
to this:
// Width and height decreased by 1
int maxHeight = sourceHeight - 1;
int maxWidth = sourceWidth - 1;
Well, I've met a very strange thing, which might be or might be not a souce of the problem.
I've started to try implementing convolution matrix by myself and encountered strange behaviour. I was testing code on a small image 4x4 pixels. The code is following:
var source = Bitmap.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\Безымянный.png");
using (FastBitmap sourceBitmap = new FastBitmap(source))
{
for (int TY = 0; TY < 4; TY++)
{
for (int TX = 0; TX < 4; TX++)
{
Color color = sourceBitmap.GetPixel(TX, TY);
Console.Write(color.B.ToString().PadLeft(5));
}
Console.WriteLine();
}
}
Althought I'm printing out only blue channel value, it's still clearly incorrect.
On the other hand, your solution partitially works, what makes the thing I've found kind of irrelevant. One more guess I have: what is your system's DPI?
From what I have found helpfull, here are some links:
C++ implementation of bicubic interpolation on
matrix
C# implemetation of bicubic interpolation, lacking the part about rescaling
Thread on gamedev.net which has almost working solution
That's my answer so far, but I will try further.
i am trying to merge two textures into one in unity
the first texture is from a webcamTexture
the second is from a sprite using : gameobject.getComponent<SpriteRenderer>().sprite.texture as Texture2D
I'm having problem in writing the function this is what i did so far :
public static Texture2D CombineTextures(GameObject obj, Texture2D background, Texture2D TodrawLogo)
{
Vector3 v = obj.transform.position;// obj is TodrawLogo gameobject
int width = TodrawLogo.width;
int height = TodrawLogo.height;
for (int x =(int)v.x; x < width; x++){
background.SetPixel(x,(int)v.y,TodrawLogo.GetPixel(x,(int)v.y));
}
background.Apply();
return background;
}
this what i am trying to do :
WebcamTexture
the result Texture should be like this
the webcamTexture is a 3dplane and the logo is a single sprite
but sadly my function doesn't work
does anyone know how to fix this
I know that i should find the exact coordinate of the todraw image and set the pixels but i can't figure out how
Much appreciation
EDIT:
i tried to use #nexx code :
public static Texture2D CombineTexture(Texture2D background, Texture2D TodrawLogo)
{
int width = TodrawLogo.width;
int height = TodrawLogo.height;
int backWidth = background.width;
int backHeight = background.height;
// bottom right corner
int startX = backWidth - width;
int startY = backHeight - height;
// loop through texture
int y = 0;
while (y < backHeight) {
int x = 0;
while (x < backWidth) {
// set normal pixels
background.SetPixel(x,y,background.GetPixel(x,y));
// if we are at bottom right apply logo
//TODO also check alpha, if there is no alpha apply it!
if(x >= startX && y < backHeight- startY)
background.SetPixel(x,y,TodrawLogo.GetPixel(x-startX,y-startY));
++x;
}
++y;
}
background.Apply();
return background;
}
but this is the resulting image i get :
i am stuck at this can someone please tell me what am i doing wrong ?
Here's a working example. I tested!
public Texture2D AddWatermark(Texture2D background, Texture2D watermark)
{
int startX = 0;
int startY = background.height - watermark.height;
for (int x = startX; x < background.width; x++)
{
for (int y = startY; y < background.height; y++)
{
Color bgColor = background.GetPixel(x, y);
Color wmColor = watermark.GetPixel(x - startX, y - startY);
Color final_color = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
background.SetPixel(x, y, final_color);
}
}
background.Apply();
return background;
}
This code works perfectly with two images that are not the same size
public static Texture2D AddWatermark(Texture2D background, Texture2D watermark, int startX, int startY)
{
Texture2D newTex = new Texture2D(background.width, background.height, background.format, false);
for (int x = 0; x < background.width; x++)
{
for (int y = 0; y < background.height; y++)
{
if (x >= startX && y >= startY && x < watermark.width && y < watermark.height)
{
Color bgColor = background.GetPixel(x, y);
Color wmColor = watermark.GetPixel(x - startX, y - startY);
Color final_color = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
newTex.SetPixel(x, y, final_color);
}
else
newTex.SetPixel(x, y, background.GetPixel(x, y));
}
}
newTex.Apply();
return newTex;
}
I made some changes for your CombineTextures method,
public static Texture2D CombineTexture(GameObject obj, Texture2D background, Texture2D TodrawLogo)
{
int width = TodrawLogo.width;
int height = TodrawLogo.height;
int backWidth = background.width;
int backHeight = background.height;
// bottom right corner
int startX = backWidth - width;
int startY = backHeight - height;
// loop through texture
int y = 0;
while (y < backHeight) {
int x = 0;
while (x < backWidth) {
// set normal pixels
background.SetPixel(x,y,background.GetPixel(x,y));
// if we are at bottom right apply logo
//TODO also check alpha, if there is no alpha apply it!
if(x >= startX && y < backHeight- startY)
background.SetPixel(x,y,TodrawLogo.GetPixel(x-startX,y-startY));
++x;
}
++y;
}
background.Apply();
return background;
}
You can change the values inside while loop to place your texture where you want.
I don't know how you would like to do it but you can use unity's OnGUI method to draw the logo where needed.
http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnGUI.html
Its as simple as something like this:
var aTexture : Texture;
function OnGUI() {
if(!aTexture){
Debug.LogError("Assign a Texture in the inspector.");
return;
}
GUI.DrawTexture(Rect(10,10,60,60), aTexture, ScaleMode.ScaleToFit, true, 10.0f);
}
I hope i was helpful.
Try this (its a modification of nexx's code.) It combines two textures into a new texture. The new texture is guarenteed to be writable (otherwise SetPixel/GetPixel won't work.)
Also, it assumes that the watermark texture is smaller than the background texture.
Please note: I've not tested this.
public static Texture2D AddWatermark(Texture2D background, Texture2D watermark)
{
// Create a new writable texture.
Texture2D result = new Texture2D(background.width, background.height);
// Draw watermark at bottom right corner.
int startX = background.width - watermark.width;
int startY = background.height - watermark.height;
for (int x = 0; x < background.width; x++) {
for (int y = 0; y < background.height; y++) {
Color bgColor = background.GetPixel(x, y);
Color wmColor = new Color(0, 0, 0, 0);
// Change this test if no longer drawing at the bottom right corner.
if (x >= startX && y >= startY) {
wmColor = watermark.GetPixel(x, y);
}
// Alpha-blend background and watermark color.
Color bended = bgColor * (1.0f - wmColor.a) + wmColor;
blended.a = 1.0f;
result.SetPixel(x, y, blended);
}
}
result.Apply();
return result;
}
A slightly more optimized solution. Reading and writing only the area required for the watermark.
public static Texture2D AddWatermark(Texture2D background, Texture2D watermark, int startPositionX, int startPositionY)
{
//only read and rewrite the area of the watermark
for (int x = startPositionX; x < background.width; x++)
{
for (int y = startPositionY; y < background.height; y++)
{
if (x - startPositionX < watermark.width && y - startPositionY < watermark.height)
{
var bgColor = background.GetPixel(x, y);
var wmColor = watermark.GetPixel(x - startPositionX, y - startPositionY);
var finalColor = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
background.SetPixel(x, y, finalColor);
}
}
}
background.Apply();
return background;
}