XNA - How can I draw my vertices more efficiently? - performance

Using the code below,
1 maxed out mesh draws at 60 FPS,
2 maxed out meshes draw at 33~ FPS,
3 maxed out meshes draw at 28~ FPS,
4 maxed out meshes draw at 20~ FPS.
Am I doing something wrong, or am I reaching some sort of limit? It doesn't seem like I am drawing a lot of polygons but I am still new to programming so I don't know much. Please offer some efficiency advice. Thank you.
class PolygonManager
{
List<List<VertexPositionColor>> vertices;
VertexBuffer vertexBuffer;
List<List<int>> indices;
IndexBuffer indexBuffer;
int meshRef;
int indexRef;
Random random;
public PolygonManager()
{
vertices = new List<List<VertexPositionColor>>();
vertices.Add(new List<VertexPositionColor>());
indices = new List<List<int>>();
indices.Add(new List<int>());
meshRef = -1;
indexRef = 0;
random = new Random();
}
public void CreateMesh(int length, int width, Vector3 position, Color color)
{
meshRef = -1;
indexRef = 0;
for (int i = 0; i < vertices.Count; i++)
{
if (vertices[i].Count <= 65536 - (length * width))
meshRef = i;
}
if (meshRef == -1)
{
vertices.Add(new List<VertexPositionColor>());
indices.Add(new List<int>());
meshRef = vertices.Count - 1;
}
indexRef = vertices[meshRef].Count;
for (int y = 0; y < length; y++)
{
for (int x = 0; x < width; x++)
{
vertices[meshRef].Add(new VertexPositionColor(new Vector3(x, 0, y) + position,
new Color(color.R + (random.Next(-10, 10) / 100), color.G + (random.Next(-10, 10) / 100), color.B + (random.Next(-10, 10) / 100))));
}
}
for (int y = 0; y < length - 1; y++)
{
for (int x = 0; x < width - 1; x++)
{
int topLeft = x + y * width;
int topRight = (x + 1) + y * width;
int lowerLeft = x + (y + 1) * width;
int lowerRight = (x + 1) + (y + 1) * width;
indices[meshRef].Add(topLeft + indexRef);
indices[meshRef].Add(lowerRight + indexRef);
indices[meshRef].Add(lowerLeft + indexRef);
indices[meshRef].Add(topLeft + indexRef);
indices[meshRef].Add(topRight + indexRef);
indices[meshRef].Add(lowerRight + indexRef);
}
}
}
public void Draw(GraphicsDevice graphicsDevice, BasicEffect basicEffect)
{
for (int v = 0; v < vertices.Count; v++)
{
vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), vertices[v].Count, BufferUsage.WriteOnly);
vertexBuffer.SetData<VertexPositionColor>(vertices[v].ToArray());
graphicsDevice.SetVertexBuffer(vertexBuffer);
indexBuffer = new IndexBuffer(graphicsDevice, typeof(int), indices[v].Count, BufferUsage.WriteOnly);
indexBuffer.SetData<int>(indices[v].ToArray());
graphicsDevice.Indices = indexBuffer;
foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
{
effectPass.Apply();
for (int i = 0; i < 6; i++)
{
graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices[v].Count, 0, indices[v].Count/3);
}
}
}
}
}

Moving the code where you initialize the buffers and write the data outside of the draw method should increase performance significantly.
Creating vertex and index buffers is an expensive operation. For static meshes (where the vertices don't change) you can reuse the buffers.
If the vertices/indices change often (once per frame) use a dynamic buffer.

Related

Unity line renderer smooth algorithm

I need an !effective! algorithm to smooth a line renderer (basically, the given Vector3 which holds the points of the renderer)
something like that
Here is my code, but the fps with it is very low:
public static List<Vector3> MakeSmoothCurve(Vector3[] arrayToCurve, float smoothness)
{
List<Vector3> points;
List<Vector3> curvedPoints;
int pointsLength = 0;
int curvedLength = 0;
if (smoothness < 1.0f) smoothness = 1.0f;
pointsLength = arrayToCurve.Length;
curvedLength = (pointsLength * Mathf.RoundToInt(smoothness)) - 1;
curvedPoints = new List<Vector3>(curvedLength);
float t = 0.0f;
for (int pointInTimeOnCurve = 0; pointInTimeOnCurve < curvedLength + 1; pointInTimeOnCurve++)
{
t = Mathf.InverseLerp(0, curvedLength, pointInTimeOnCurve);
points = new List<Vector3>(arrayToCurve);
for (int j = pointsLength - 1; j > 0; j--)
{
for (int i = 0; i < j; i++)
{
points[i] = (1 - t) * points[i] + t * points[i + 1];
}
}
curvedPoints.Add(points[0]);
}
return (curvedPoints);
}
You can use a CurveField
https://docs.unity3d.com/ScriptReference/EditorGUILayout.CurveField.html
With that you can easily edit/test your curve and retrieve a point at given time.
https://docs.unity3d.com/ScriptReference/AnimationCurve.Evaluate.html

Spread / Space Out the Values of Perlin Noise

I am working to create procedurally generated terrain using Perlin Noise. I have gotten it to work fairly well so far. However, the landscape produced seems more like a swampland with many smaller lakes and very few real oceans.
I currently have the value noise scale value, but it only zooms the map in. Thinking about Perlin Noise like a sin wave, dividing by a value should spread it out more, like increasing the period of the sin wave. However, this is all that happens when I add this and increase that value (period is currently set at 1.25 in this picture):
This is what it looks like if I increase the sea level:
How can I make it so that there are more larger oceans and, or is this simply a bad way of looking at it? Here is my map generation code (as is):
public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity,
Vector2 offset, NormalizeMode normalizeMode, float normalizationEstimation) {
float[,] noiseMap = new float[mapWidth,mapHeight];
System.Random prng = new System.Random (seed);
Vector2[] octaveOffsets = new Vector2[octaves];
float maxPossibleHeight = 0;
float amplitude = 1;
float frequency = 1;
for (int i = 0; i < octaves; i++) {
float offsetX = prng.Next (-100000, 100000) + offset.x;
float offsetY = prng.Next (-100000, 100000) - offset.y;
octaveOffsets [i] = new Vector2 (offsetX, offsetY);
maxPossibleHeight += amplitude;
amplitude *= persistance;
}
if (scale <= 0) {
scale = 0.0001f;
}
float maxLocalNoiseHeight = float.MinValue;
float minLocalNoiseHeight = float.MaxValue;
float halfWidth = mapWidth / 2f;
float halfHeight = mapHeight / 2f;
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
amplitude = 1;
frequency = 1;
float noiseHeight = 0;
for (int i = 0; i < octaves; i++) {
float sampleX = (x - halfWidth + octaveOffsets[i].x) / scale * frequency;
float sampleY = (y - halfHeight + octaveOffsets[i].y) / scale * frequency;
float perlinValue = ((Mathf.PerlinNoise (sampleX, sampleY) / period) * 2 - 1);
noiseHeight += perlinValue * amplitude;
amplitude *= persistance;
frequency *= lacunarity;
}
if (noiseHeight > maxLocalNoiseHeight) {
maxLocalNoiseHeight = noiseHeight;
} else if (noiseHeight < minLocalNoiseHeight) {
minLocalNoiseHeight = noiseHeight;
}
noiseMap [x, y] = noiseHeight;
}
}
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
if (normalizeMode == NormalizeMode.Local) {
noiseMap [x, y] = Mathf.InverseLerp (minLocalNoiseHeight, maxLocalNoiseHeight, noiseMap [x, y]);
} else {
float normalizedHeight = (noiseMap [x, y] + 1) / (2f * maxPossibleHeight / normalizationEstimation);
noiseMap [x, y] = Mathf.Clamp(normalizedHeight, 0, int.MaxValue);
}
}
}
return noiseMap;
}

I made a processing program that generates a mandelbrot set but don't know how to effectively implement a zoom method

I'm not sure if it is possible in processing but I would like to be able to zoom in on the fractal without it being extremely laggy and buggy. What I currently have is:
int maxIter = 100;
float zoom = 1;
float x0 = width/2;
float y0 = height/2;
void setup(){
size(500,300);
noStroke();
smooth();
}
void draw(){
translate(x0, y0);
scale(zoom);
for(float Py = 0; Py < height; Py++){
for(float Px = 0; Px < width; Px++){
// scale pixel coordinates to Mandelbrot scale
float w = width;
float h = height;
float xScaled = (Px * (3.5/w)) - 2.5;
float yScaled = (Py * (2/h)) - 1;
float x = 0;
float y = 0;
int iter = 0;
while( x*x + y*y < 2*2 && iter < maxIter){
float tempX = x*x - y*y + xScaled;
y = 2*x*y + yScaled;
x = tempX;
iter += 1;
}
// color pixels
color c;
c = pickColor(iter);
rect(Px, Py,1,1);
fill(c);
}
}
}
// pick color based on time pixel took to escape (number of iterations through loop)
color pickColor(int iters){
color b = color(0,0,0);
if(iters == maxIter) return b;
int l = 1;
color[] colors = new color[maxIter];
for(int i = 0; i < colors.length; i++){
switch(l){
case 1 : colors[i] = color(255,0,0); break;
case 2 : colors[i] = color(0,0,255); break;
case 3 : colors[i] = color(0,255,0); break;
}
if(l == 1 || l == 2) l++;
else if(l == 3) l = 1;
else l--;
}
return colors[iters];
}
// allow zooming in and out
void mouseWheel(MouseEvent event){
float direction = event.getCount();
if(direction < 0) zoom += .02;
if(direction > 0) zoom -= .02;
}
// allow dragging back and forth to change view
void mouseDragged(){
x0+= mouseX-pmouseX;
y0+= mouseY-pmouseY;
}
but it doesn't work very well. It works alright at the size and max iteration I have it set to now (but still not well) and is completely unusable at larger sizes or higher maximum iterations.
The G4P library has an example that does exactly this. Download the library and go to the G4P_MandelBrot example. The example can be found online here.
Hope this helps!

Rescaling image in J2ME

I'm resizing a bird image using the following code:
private Image resizeImage(Image src) {
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
int screenWidth=getWidth()/3;
int screenHeight=getHeight()/3;
Image tmp = Image.createImage(screenWidth, srcHeight);
Graphics g = tmp.getGraphics();
int ratio = (srcWidth << 16) / screenWidth;
int pos = ratio/2;
//Horizontal Resize
for (int x = 0; x < screenWidth; x++) {
g.setClip(x, 0, 1, srcHeight);
g.drawImage(src, x - (pos >> 16), 0, Graphics.LEFT | Graphics.TOP);
pos += ratio;
}
Image resizedImage = Image.createImage(screenWidth, screenHeight);
g = resizedImage.getGraphics();
ratio = (srcHeight << 16) / screenHeight;
pos = ratio/2;
//Vertical resize
for (int y = 0; y < screenHeight; y++) {
g.setClip(0, y, screenWidth, 1);
g.drawImage(tmp, 0, y - (pos >> 16), Graphics.LEFT | Graphics.TOP);
pos += ratio;
}
return resizedImage;
}
The image is resized but it has white background along with it as shown. How to get only resized image with transparent background..?
Here is an Image scaling function I've been using. Includes transparency. Found here: http://willperone.net/Code/codescaling.php
public Image scale(Image original, int newWidth, int newHeight) {
int[] rawInput = new int[original.getHeight() * original.getWidth()];
original.getRGB(rawInput, 0, original.getWidth(), 0, 0, original.getWidth(), original.getHeight());
int[] rawOutput = new int[newWidth * newHeight];
// YD compensates for the x loop by subtracting the width back out
int YD = (original.getHeight() / newHeight) * original.getWidth() - original.getWidth();
int YR = original.getHeight() % newHeight;
int XD = original.getWidth() / newWidth;
int XR = original.getWidth() % newWidth;
int outOffset = 0;
int inOffset = 0;
for (int y = newHeight, YE = 0; y > 0; y--) {
for (int x = newWidth, XE = 0; x > 0; x--) {
rawOutput[outOffset++] = rawInput[inOffset];
inOffset += XD;
XE += XR;
if (XE >= newWidth) {
XE -= newWidth;
inOffset++;
}
}
inOffset += YD;
YE += YR;
if (YE >= newHeight) {
YE -= newHeight;
inOffset += original.getWidth();
}
}
rawInput = null;
return Image.createRGBImage(rawOutput, newWidth, newHeight, true);
}

Finding a Tangent Line at a Point on a Randomized Slope

I have a piece of processing code that I was given, which appears to be setting up a randomized Fourier series. Unfortunately, despite my efforts to improve my mathematical skills, I have no idea what it is doing and the articles I have found are not much help.
I'm trying to extend this code so that I can draw a line tangent to a point on the slope created by the code bellow. The closest I can find to answering this is in the mathematics forum. Unfortunately, I don't really understand what is being discussed or if it really is relevant to my situation.
Any assistance on how I would go about calculating a tangent line at a particular point on this curve would be much appreciated.
UPDATE As of 06/17/13
I've been trying to play around with this, but without much success. This is the best I can do, and I doubt that I'm applying the derivative correctly to find the tangent (or even if I have found the derivative at the point correctly). Also, I'm beginning to worry that I'm not drawing the line correctly even if I have everything else correct. If anyone can provide input on this I'd appreciate it.
final int w = 800;
final int h = 480;
double[] skyline;
PImage img;
int numOfDeriv = 800;
int derivModBy = 1; //Determines how many points will be checked
int time;
int timeDelay = 1000;
int iter;
double[] derivatives;
void setup() {
noStroke();
size(w, h);
fill(0,128,255);
rect(0,0,w,h);
int t[] = terrain(w,h);
fill(77,0,0);
for(int i=0; i < w; i++){
rect(i, h, 1, -1*t[i]);
}
time = millis();
timeDelay = 100;
iter =0;
img = get();
}
void draw() {
int dnum = 0; //Current position of derivatives
if(iter == numOfDeriv) iter = 0;
if (millis() > time + timeDelay){
image(img, 0, 0, width, height);
strokeWeight(4);
stroke(255,0,0);
point((float)iter*derivModBy, height-(float)skyline[iter*derivModBy]);
strokeWeight(1);
stroke(255,255,0);
print("At x = ");
print(iter);
print(", y = ");
print(skyline[iter]);
print(", derivative = ");
print((float)derivatives[iter]);
print('\n');
lineAngle(iter, (int)(height-skyline[iter]), (float)derivatives[iter], 100);
lineAngle(iter, (int)(height-skyline[iter]), (float)derivatives[iter], -100);
stroke(126);
time = millis();
iter += 1;
}
}
void lineAngle(int x, int y, float angle, float length)
{
line(x, y, x+cos(angle)*length, y-sin(angle)*length);
}
int[] terrain(int w, int h){
width = w;
height = h;
//min and max bracket the freq's of the sin/cos series
//The higher the max the hillier the environment
int min = 1, max = 6;
//allocating horizon for screen width
int[] horizon = new int[width];
skyline = new double[width];
derivatives = new double[numOfDeriv];
//ratio of amplitude of screen height to landscape variation
double r = (int) 2.0/5.0;
//number of terms to be used in sine/cosine series
int n = 4;
int[] f = new int[n*2];
//calculating omegas for sine series
for(int i = 0; i < n*2 ; i ++){
f[i] = (int) random(max - min + 1) + min;
}
//amp is the amplitude of the series
int amp = (int) (r*height);
int dnum = 0; //Current number of derivatives
for(int i = 0 ; i < width; i ++){
skyline[i] = 0;
double derivative = 0.0;
for(int j = 0; j < n; j++){
if(i % derivModBy == 0){
derivative += ( cos( (f[j]*PI*i/height) * f[j]*PI/height) -
sin(f[j+n]*PI*i/height) * f[j+n]*PI/height);
}
skyline[i] += ( sin( (f[j]*PI*i/height) ) + cos(f[j+n]*PI*i/height) );
}
skyline[i] *= amp/(n*2);
skyline[i] += (height/2);
skyline[i] = (int)skyline[i];
horizon[i] = (int)skyline[i];
derivative *= amp/(n*2);
if(i % derivModBy == 0){
derivatives[dnum++] = derivative;
derivative = 0;
}
}
return horizon;
}
void reset() {
time = millis();
}
Well it seems in this particular case that you don't need to understand much about the Fourier Series, just that it has the form:
A0 + A1*cos(x) + A2*cos(2*x) + A3*cos(3*x) +... + B1*sin(x) + B2*sin(x) +...
Normally you're given a function f(x) and you need to find the values of An and Bn such that the Fourier series converges to your function (as you add more terms) for some interval [a, b].
In this case however they want a random function that just looks like different lumps and pits (or hills and valleys as the context might suggest) so they choose random terms from the Fourier Series between min and max and set their coefficients to 1 (and conceptually 0 otherwise). They also satisfy themselves with a Fourier series of 4 sine terms and 4 cosine terms (which is certainly easier to manage than an infinite number of terms). This means that their Fourier Series ends up looking like different sine and cosine functions of different frequencies added together (and all have the same amplitude).
Finding the derivative of this is easy if you recall that:
sin(n*x)' = n * cos(x)
cos(n*x)' = -n * sin(x)
(f(x) + g(x))' = f'(x) + g'(x)
So the loop to calculate the the derivative would look like:
for(int j = 0; j < n; j++){
derivative += ( cos( (f[j]*PI*i/height) * f[j]*PI/height) - \
sin(f[j+n]*PI*i/height) * f[j+n]*PI/height);
}
At some point i (Note the derivative is being taken with respect to i since that is the variable that represents our x position here).
Hopefully with this you should be able to calculate the equation of the tangent line at a point i.
UPDATE
At the point where you do skyline[i] *= amp/(n*2); you must also adjust your derivative accordingly derivative *= amp/(n*2); however your derivative does not need adjusting when you do skyline[i] += height/2;
I received an answer to this problem via "quarks" on processing.org form. Essentially the problem is that I was taking the derivative of each term of the series instead of taking the derivative of the sum of the entire series. Also, I wasn't applying my result correctly anyway.
Here is the code that quarks provided that definitively solves this problem.
final int w = 800;
final int h = 480;
float[] skyline;
PImage img;
int numOfDeriv = 800;
int derivModBy = 1; //Determines how many points will be checked
int time;
int timeDelay = 1000;
int iter;
float[] tangents;
public void setup() {
noStroke();
size(w, h);
fill(0, 128, 255);
rect(0, 0, w, h);
terrain(w, h);
fill(77, 0, 0);
for (int i=0; i < w; i++) {
rect(i, h, 1, -1*(int)skyline[i]);
}
time = millis();
timeDelay = 100;
iter =0;
img = get();
}
public void draw() {
if (iter == numOfDeriv) iter = 0;
if (millis() > time + timeDelay) {
image(img, 0, 0, width, height);
strokeWeight(4);
stroke(255, 0, 0);
point((float)iter*derivModBy, height-(float)skyline[iter*derivModBy]);
strokeWeight(1);
stroke(255, 255, 0);
print("At x = ");
print(iter);
print(", y = ");
print(skyline[iter]);
print(", derivative = ");
print((float)tangents[iter]);
print('\n');
lineAngle(iter, (int)(height-skyline[iter]), (float)tangents[iter], 100);
lineAngle(iter, (int)(height-skyline[iter]), (float)tangents[iter], -100);
stroke(126);
time = millis();
iter += 1;
}
}
public void lineAngle(int x, int y, float angle, float length) {
line(x, y, x+cos(angle)*length, y-sin(angle)*length);
}
public void terrain(int w, int h) {
//min and max bracket the freq's of the sin/cos series
//The higher the max the hillier the environment
int min = 1, max = 6;
skyline = new float[w];
tangents = new float[w];
//ratio of amplitude of screen height to landscape variation
double r = (int) 2.0/5.0;
//number of terms to be used in sine/cosine series
int n = 4;
int[] f = new int[n*2];
//calculating omegas for sine series
for (int i = 0; i < n*2 ; i ++) {
f[i] = (int) random(max - min + 1) + min;
}
//amp is the amplitude of the series
int amp = (int) (r*h);
for (int i = 0 ; i < w; i ++) {
skyline[i] = 0;
for (int j = 0; j < n; j++) {
skyline[i] += ( sin( (f[j]*PI*i/h) ) + cos(f[j+n]*PI*i/h) );
}
skyline[i] *= amp/(n*2);
skyline[i] += (h/2);
}
for (int i = 1 ; i < w - 1; i ++) {
tangents[i] = atan2(skyline[i+1] - skyline[i-1], 2);
}
tangents[0] = atan2(skyline[1] - skyline[0], 1);
tangents[w-1] = atan2(skyline[w-2] - skyline[w-1], 1);
}
void reset() {
time = millis();
}

Resources