Saturday, July 28, 2018

c# - XNA shield effect with a Primative sphere problem


I'm having issue with a shield effect i'm trying to develop. I want to do a shield effect that surrounds part of a model like this: http://i.imgur.com/jPvrf.png


I currently got this: http://i.imgur.com/Jdin7.png (The red likes are a simple texture a black background with a red cross in it, for testing purposes: http://i.imgur.com/ODtzk.png where the smaller cross in the middle shows the contact point)



This sphere is drawn via a primitive (DrawIndexedPrimitives)


This is how i calculate the pieces of the sphere using a class i've called Sphere


(this class is based off the code here: http://xbox.create.msdn.com/en-US/education/catalog/sample/primitives_3d)


public class Sphere { // During the process of constructing a primitive model, vertex // and index data is stored on the CPU in these managed lists. List vertices = new List(); List indices = new List();


    // Once all the geometry has been specified, the InitializePrimitive
// method copies the vertex and index data into these buffers, which
// store it on the GPU ready for efficient rendering.
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
BasicEffect basicEffect;


public Vector3 position = Vector3.Zero;
public Matrix RotationMatrix = Matrix.Identity;

public Texture2D texture;

///
/// Constructs a new sphere primitive,
/// with the specified size and tessellation level.
///


public Sphere(float diameter, int tessellation, Texture2D text, float up, float down, float portstar, float frontback)
{
texture = text;
if (tessellation < 3)
throw new ArgumentOutOfRangeException("tessellation");

int verticalSegments = tessellation;
int horizontalSegments = tessellation * 2;

float radius = diameter / 2;


// Start with a single vertex at the bottom of the sphere.
AddVertex(Vector3.Down * ((radius / up) + 1), Vector3.Down, Vector2.Zero);//bottom position5

// Create rings of vertices at progressively higher latitudes.
for (int i = 0; i < verticalSegments - 1; i++)
{
float latitude = ((i + 1) * MathHelper.Pi /
verticalSegments) - MathHelper.PiOver2;


float dy = (float)Math.Sin(latitude / up);//(up)5
float dxz = (float)Math.Cos(latitude);

// Create a single ring of vertices at this latitude.
for (int j = 0; j < horizontalSegments; j++)
{
float longitude = j * MathHelper.TwoPi / horizontalSegments;

float dx = (float)(Math.Cos(longitude) * dxz) / portstar;//port and starboard (right)2
float dz = (float)(Math.Sin(longitude) * dxz) * frontback;//front and back1.4


Vector3 normal = new Vector3(dx, dy, dz);

AddVertex(normal * radius, normal, new Vector2(j, i));
}
}

// Finish with a single vertex at the top of the sphere.
AddVertex(Vector3.Up * ((radius / down) + 1), Vector3.Up, Vector2.One);//top position5


// Create a fan connecting the bottom vertex to the bottom latitude ring.
for (int i = 0; i < horizontalSegments; i++)
{
AddIndex(0);
AddIndex(1 + (i + 1) % horizontalSegments);
AddIndex(1 + i);
}

// Fill the sphere body with triangles joining each pair of latitude rings.
for (int i = 0; i < verticalSegments - 2; i++)

{
for (int j = 0; j < horizontalSegments; j++)
{
int nextI = i + 1;
int nextJ = (j + 1) % horizontalSegments;

AddIndex(1 + i * horizontalSegments + j);
AddIndex(1 + i * horizontalSegments + nextJ);
AddIndex(1 + nextI * horizontalSegments + j);


AddIndex(1 + i * horizontalSegments + nextJ);
AddIndex(1 + nextI * horizontalSegments + nextJ);
AddIndex(1 + nextI * horizontalSegments + j);
}
}

// Create a fan connecting the top vertex to the top latitude ring.
for (int i = 0; i < horizontalSegments; i++)
{
AddIndex(CurrentVertex - 1);

AddIndex(CurrentVertex - 2 - (i + 1) % horizontalSegments);
AddIndex(CurrentVertex - 2 - i);
}

//InitializePrimitive(graphicsDevice);
}

///
/// Adds a new vertex to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.

///

protected void AddVertex(Vector3 position, Vector3 normal, Vector2 texturecoordinate)
{
vertices.Add(new VertexPositionNormal(position, normal, texturecoordinate));
}


///
/// Adds a new index to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.

///

protected void AddIndex(int index)
{
if (index > ushort.MaxValue)
throw new ArgumentOutOfRangeException("index");

indices.Add((ushort)index);
}



///
/// Queries the index of the current vertex. This starts at
/// zero, and increments every time AddVertex is called.
///

protected int CurrentVertex
{
get { return vertices.Count; }
}

public void InitializePrimitive(GraphicsDevice graphicsDevice)

{
// Create a vertex declaration, describing the format of our vertex data.

// Create a vertex buffer, and copy our vertex data into it.
vertexBuffer = new VertexBuffer(graphicsDevice,
typeof(VertexPositionNormal),
vertices.Count, BufferUsage.None);

vertexBuffer.SetData(vertices.ToArray());


// Create an index buffer, and copy our index data into it.
indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
indices.Count, BufferUsage.None);

indexBuffer.SetData(indices.ToArray());

// Create a BasicEffect, which will be used to render the primitive.
basicEffect = new BasicEffect(graphicsDevice);
//basicEffect.EnableDefaultLighting();
}


///
/// Draws the primitive model, using the specified effect. Unlike the other
/// Draw overload where you just specify the world/view/projection matrices
/// and color, this method does not set any renderstates, so you must make
/// sure all states are set to sensible values before you call it.
///

public void Draw(Effect effect)
{
GraphicsDevice graphicsDevice = effect.GraphicsDevice;


// Set our vertex declaration, vertex buffer, and index buffer.
graphicsDevice.SetVertexBuffer(vertexBuffer);

graphicsDevice.Indices = indexBuffer;

graphicsDevice.BlendState = BlendState.Additive;

foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
{

effectPass.Apply();

int primitiveCount = indices.Count / 3;

graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
vertices.Count, 0, primitiveCount);

}
graphicsDevice.BlendState = BlendState.Opaque;
}



///
/// Draws the primitive model, using a BasicEffect shader with default
/// lighting. Unlike the other Draw overload where you specify a custom
/// effect, this method sets important renderstates to sensible values
/// for 3D model rendering, so you do not need to set these states before
/// you call it.
///

public void Draw(Camera camera, Color color)

{
// Set BasicEffect parameters.
basicEffect.World = GetWorld();
basicEffect.View = camera.view;
basicEffect.Projection = camera.projection;
basicEffect.DiffuseColor = color.ToVector3();
basicEffect.TextureEnabled = true;
basicEffect.Texture = texture;



GraphicsDevice device = basicEffect.GraphicsDevice;
device.DepthStencilState = DepthStencilState.Default;

if (color.A < 255)
{
// Set renderstates for alpha blended rendering.
device.BlendState = BlendState.AlphaBlend;
}
else
{

// Set renderstates for opaque rendering.
device.BlendState = BlendState.Opaque;
}

// Draw the model, using BasicEffect.
Draw(basicEffect);
}

public virtual Matrix GetWorld()
{

return /*world */ Matrix.CreateScale(1f) * RotationMatrix * Matrix.CreateTranslation(position);
}
}



public struct VertexPositionNormal : IVertexType
{
public Vector3 Position;
public Vector3 Normal;

public Vector2 TextureCoordinate;


///
/// Constructor.
///

public VertexPositionNormal(Vector3 position, Vector3 normal, Vector2 textCoor)
{
Position = position;
Normal = normal;

TextureCoordinate = textCoor;
}

///
/// A VertexDeclaration object, which contains information about the vertex
/// elements contained within this struct.
///

public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),

new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
);

VertexDeclaration IVertexType.VertexDeclaration
{
get { return VertexPositionNormal.VertexDeclaration; }
}

}


A simple call to the class to initialise it. The Draw method is called in the master draw method in the Gamecomponent.


My current thoughts on this are:



  1. The direction of the weapon hitting the ship is used to get the middle position for the texture

  2. Wrap a texture around the drawn sphere based on this point of contact


Problem is i'm not sure how to do this. Can anyone help or if you have a better idea please tell me i'm open for opinion? :-) Thanks.



Answer



Looks like you're on the right track. I did this before in a game, and instead of changing where on the texture the impact is centered, I merely rotated the sphere. I also used a procedural shader instead of a texture. Here's some of the code I used (public domain, do whatever you want with it).



EDIT: Uploaded a video of it: http://www.youtube.com/watch?v=DqKkaJHf1gg


(NOTE: This code is for XNA 3.1, and has a couple calls to internal functions, so it won't work out of the box. It also does a lot of allocation, so will not perform well on the 360)


[SingletonRenderer]
public sealed class ShieldRenderer : Renderer
{
private static readonly Texture3D _noiseTexture = SpacerGame.load("textures/noise");

///
/// Since the default render state culls back-facing edges (i.e. those with normals opposite the
/// viewer), the shader is not see-through. This could be fixed by order-independent trasnparency

/// or disabling backface culling, but since we're only going to be seeing the shader from one
/// direction, it's easier (and faster, and provides more control) to fake the effect by rotating
/// the sphere a bit towards the camera.
///

private const float ROTATION_Y = -MathHelper.Pi * 3 / 8;
private const float NOISE_SPEED = 0.15f;
private const float IMPACT_TIME = 1.0f;

private readonly Sphere _sphere;
private readonly ShieldShader _shader;

private readonly ReaderWriterCollection, ShieldImpact> _impacts;
private readonly ShieldParameters _params;

public ShieldRenderer() : base(RenderPass.SHIELDS)
{
_params = Effects.initShieldRenderer(this);
_sphere = new Sphere(1, 20);
_shader = new ShieldShader
{
noise = _noiseTexture,

speed = NOISE_SPEED,
};
_impacts = new ReaderWriterCollection, ShieldImpact>();
}

public override void draw(DeltaT dt)
{
_impacts.synchronize();
foreach(ShieldImpact impact in _impacts)
{

// Update time
if(impact.startTime == 0) impact.startTime = dt.totalActual;
else impact.time += dt.dtAt(impact.target.pos);

// Kill off dead impacts
if (impact.time > IMPACT_TIME)
{
_impacts.Remove(impact);
continue;
}


// Skip offscreen targets
if(!impact.target.bounds.isPartiallyOnScreen())
continue;

_shader.worldViewProj = impact.baseTransform *
impact.target.pos.toScreenWvpMatrix();
_shader.startTime = impact.startTime;
_shader.color = impact.color;
_shader.time = impact.time / IMPACT_TIME;


_shader.begin();
_sphere.draw(_shader.shield);
_shader.end();
}
}

protected override void dispose()
{
base.dispose();

_sphere.Dispose();
_shader.Dispose();
}

protected override void finalize()
{
base.finalize();
_impacts.synchronize();
_impacts.Clear();
_impacts.Dispose();

}

public void addImpact(Entity target, float direction, float shieldStrength)
{
// TODO -- figurre out right shield radius
_impacts.Add(new ShieldImpact
{
target = target,
color = _params.colors.sample(shieldStrength).ToVector3(),
baseTransform = Matrix.CreateScale(target.size.X) *

Matrix.CreateRotationY(ROTATION_Y) *
Matrix.CreateRotationZ(-direction),
});
}

private sealed class ShieldImpact : ISimpleListNode
{
public Entity target;
public Matrix baseTransform;
public Vector3 color;

public float startTime;
public float time;

ShieldImpact ISimpleListNode.next { get; set; }
ShieldImpact ISimpleListNode.prev { get; set; }
}
}

public sealed class ShieldParameters
{

public ColorGradient colors;
}

Shader:


#include "common.fxh"

// @params
float4x4 _worldViewProj;
texture _noise;
float _time;

float _startTime;
float _speed;
float3 _color;
// @end

sampler sNoise = sampler_state { texture = <_noise>; magfilter = ANISOTROPIC; minfilter = ANISOTROPIC; mipfilter = ANISOTROPIC; AddressU = WRAP; AddressV = WRAP; };

PixelInfo shieldVS(float4 inPos : POSITION, float2 inUv : TEXCOORD)
{
PixelInfo p;

p.uv = inUv;
p.pos = mul(inPos, _worldViewProj);
return p;
}

static const float NOISINESS = 1;
static const float NOISE_SCALE_PRE_EXP = 1.25;
static const float NOISE_SCALE_POST_EXP = 3;
static const float NOISE_EXP = 4;


static const float DISTANCE_EXP_MIN = 0.015;
static const float DISTANCE_EXP_MAX = 0.06;
static const float DISTANCE_VALUE_CLAMP = 0.1;
static const float DISTANCE_SCALE = 144;

static const float TIME_SCALE_PRE_EXP = 3;
static const float TIME_SCALE_POST_EXP = 1;
static const float TIME_EXP = 3;

float4 shieldPS(PixelInfo p) : COLOR0

{
// Get some noise-sampled noise (a la the background)
float3 vpos;
float3 spos = float3(p.uv * NOISINESS + float2(_startTime, _startTime), _time * _speed);
vpos.x = tex3D(sNoise, spos + 0.00).r - 0.5;
vpos.y = tex3D(sNoise, spos + 0.33).r - 0.5;
vpos.z = tex3D(sNoise, spos + 0.67).r - 0.5;
float sample = tex3D(sNoise, vpos).r;
sample = pow(sample * NOISE_SCALE_PRE_EXP, NOISE_EXP) * NOISE_SCALE_POST_EXP;


// Fade out with time
float timeFactor = (pow((1 - _time) * TIME_SCALE_PRE_EXP, TIME_EXP)) * TIME_SCALE_POST_EXP;

// Glow more closer to the center; spread out over time
float d = distance(float2(smoothstep(0.125, 0.875, p.uv.x), p.uv.y), float2(0.5, 0.5));
float distanceFactor = 1 - pow(d, lerp(DISTANCE_EXP_MIN, DISTANCE_EXP_MAX, _time));
distanceFactor = smoothstep(DISTANCE_VALUE_CLAMP, 1, distanceFactor * distanceFactor * DISTANCE_SCALE);

// If alpha > 1, multiply the color by it to get an HDRish effect
float alpha = sample * timeFactor * distanceFactor;

return float4(alpha > 1 ? alpha * _color : _color, saturate(alpha));
}

technique shield
{
// @passes
pass shield { VertexShader = compile vs_1_1 shieldVS(); PixelShader = compile ps_2_0 shieldPS(); }
// @end
}


Code for sphere:


/// 
/// Simple sphere mesh generator.
///

public sealed class Sphere : IDisposable
{
private readonly VertexBuffer _vertexBuf;
private readonly IndexBuffer _indexBuf;
private readonly VertexDeclaration _vertexDecl;
private readonly int _nVerticies;

private readonly int _nFaces;

public Sphere(float radius, int slices)
{
_nVerticies = (slices + 1) * (slices + 1);
int nIndicies = 6 * slices * (slices + 1);

var indices = new int[nIndicies];
var vertices = new VertexPositionNormalTexture[_nVerticies];
float thetaStep = MathHelper.Pi / slices;

float phiStep = MathHelper.TwoPi / slices;

int iIndex = 0;
int iVertex = 0;
int iVertex2 = 0;

for (int sliceTheta = 0; sliceTheta < slices + 1; sliceTheta++)
{
float r = (float) Math.Sin(sliceTheta * thetaStep);
float y = (float) Math.Cos(sliceTheta * thetaStep);


for (int slicePhi = 0; slicePhi < (slices + 1); slicePhi++)
{
float x = r * (float) Math.Sin(slicePhi * phiStep);
float z = r * (float) Math.Cos(slicePhi * phiStep);

vertices[iVertex].Position = new Vector3(x, y, z) * radius;
vertices[iVertex].Normal = Vector3.Normalize(new Vector3(x, y, z));
vertices[iVertex].TextureCoordinate = new Vector2((float)slicePhi / slices,
(float)sliceTheta / slices);

iVertex++;

if (sliceTheta != (slices - 1))
{
indices[iIndex++] = iVertex2 + (slices + 1);
indices[iIndex++] = iVertex2 + 1;
indices[iIndex++] = iVertex2;
indices[iIndex++] = iVertex2 + (slices);
indices[iIndex++] = iVertex2 + (slices + 1);
indices[iIndex++] = iVertex2;

iVertex2++;
}
}
}

GraphicsDevice device = RenderManager.current.device;
_vertexBuf = new VertexBuffer(device, typeof(VertexPositionNormalTexture), _nVerticies, BufferUsage.None);
_indexBuf = new IndexBuffer(device, typeof(int), nIndicies, BufferUsage.None);
_vertexDecl = new VertexDeclaration(device, VertexPositionNormalTexture.VertexElements);
_vertexBuf.SetData(vertices, 0, vertices.Length);

_indexBuf.SetData(indices, 0, indices.Length);
_nFaces = nIndicies / 3;
}

public void draw(EffectPass pass)
{
GraphicsDevice device = RenderManager.current.device;
device.Indices = _indexBuf;
device.VertexDeclaration = _vertexDecl;
device.Vertices[0].SetSource(_vertexBuf, 0, VertexPositionNormalTexture.SizeInBytes);


pass.Begin();
device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _nVerticies, 0, _nFaces);
pass.End();
}

public void Dispose()
{
_vertexBuf.Dispose();
_indexBuf.Dispose();

_vertexDecl.Dispose();
}
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...