FoliageReplicator Upgrades For TSE
by J.C. Smith · in Torque Game Engine Advanced · 01/18/2007 (10:27 pm) · 64 replies
Hello all. I was looking at this resource the other day from Jeff Loveless:
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=11963
which implemented clustering and multiple foliage textures for TGE. So I decided to port that resource to TSE. The clustering works in the same way though needed some code changes for the TSE version. Along the way though I decided to do the multi foliage types using a single texture. It works by using a flag to denote if your foliage is normal or a QuadTexture (the UseQuadTexture flag in Mission Editor). So you'd just make a PNG or DDS file and split it into 4 imaginary quadrants, and paste a foliage texture in each of the 4 quadrants. If you leave the quadtexture flag unchecked then it works as normal.
As another bonus I added in some simple color variations. This will allow you to take you texture and blend some different colors with it to give some variation so all your blades of grass aren't the same color for example. That works by setting three four fields: GradientTexture, UseBlend, BlendStart and BlendEnd. If you check the UseBlend flag then it will activate this effect. BlendStart and BlendEnd are a float value from 0.0 to 1.0, which reflects which parts of the gradient texture range you want to use. The GradientTexture is a texture file that you will need to create in photoshop or whatever that stores the color range vertically. It only uses the Y pixels, and is always using the first pixel X wise. So for example if you made a 64x64 texture, and the first line started with a green color, and moving down the Y axis by the time you reached the bottom line it was yellow, and you set the BlendStart to 0 and the BlendEnd to 1 then your foliage would be colored a random shade of green to yellow. If you set the blendEnd to 0.5 then it would start at green and end at a greenishyellow variant. If your using a grayscale image, this allows you to make some nice variations on your foliage colors.
I'm pasting the source code into here in it's entirety for the four files which require changes:
shaders/FxFoliageReplicatorP.hlsl
http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=11963
which implemented clustering and multiple foliage textures for TGE. So I decided to port that resource to TSE. The clustering works in the same way though needed some code changes for the TSE version. Along the way though I decided to do the multi foliage types using a single texture. It works by using a flag to denote if your foliage is normal or a QuadTexture (the UseQuadTexture flag in Mission Editor). So you'd just make a PNG or DDS file and split it into 4 imaginary quadrants, and paste a foliage texture in each of the 4 quadrants. If you leave the quadtexture flag unchecked then it works as normal.
As another bonus I added in some simple color variations. This will allow you to take you texture and blend some different colors with it to give some variation so all your blades of grass aren't the same color for example. That works by setting three four fields: GradientTexture, UseBlend, BlendStart and BlendEnd. If you check the UseBlend flag then it will activate this effect. BlendStart and BlendEnd are a float value from 0.0 to 1.0, which reflects which parts of the gradient texture range you want to use. The GradientTexture is a texture file that you will need to create in photoshop or whatever that stores the color range vertically. It only uses the Y pixels, and is always using the first pixel X wise. So for example if you made a 64x64 texture, and the first line started with a green color, and moving down the Y axis by the time you reached the bottom line it was yellow, and you set the BlendStart to 0 and the BlendEnd to 1 then your foliage would be colored a random shade of green to yellow. If you set the blendEnd to 0.5 then it would start at green and end at a greenishyellow variant. If your using a grayscale image, this allows you to make some nice variations on your foliage colors.
I'm pasting the source code into here in it's entirety for the four files which require changes:
shaders/FxFoliageReplicatorP.hlsl
#include "shdrConsts.h"
//-----------------------------------------------------------------------------
// Structures
//-----------------------------------------------------------------------------
struct ConnectData
{
float2 texCoord : TEXCOORD0;
float4 lum : COLOR0;
float4 groundAlphaCoeff : COLOR1;
float2 alphaLookup : TEXCOORD1;
float2 blendColor : TEXCOORD2;
};
struct Fragout
{
float4 col : COLOR0;
};
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN,
uniform sampler2D diffuseMap : register(S0),
uniform sampler2D alphaMap : register(S1),
uniform sampler2D blendMap : register(S2),
uniform float4 groundAlpha : register(C1)
)
{
Fragout OUT;
float4 alpha = tex2D(alphaMap, IN.alphaLookup);
OUT.col = IN.lum * (tex2D(diffuseMap, IN.texCoord) * tex2D(blendMap, IN.blendColor));
OUT.col.a = OUT.col.a * min(alpha, groundAlpha + IN.groundAlphaCoeff.x);
return OUT;
}
#2
Change the FxFoliageItem area to look like this:
Change your vertexbuffer definition to look like:
Add these fields to the tagfieldata:
and then into the tagfieldata area below it:
01/18/2007 (10:35 pm)
Game/fx/fxfoliagereplicator.hChange the FxFoliageItem area to look like this:
class fxFoliageItem
{
public:
MatrixF Transform;
F32 Width;
F32 Height;
Box3F FoliageBox;
bool Flipped;
F32 SwayPhase;
F32 SwayTimeRatio;
F32 LightPhase;
F32 LightTimeRatio;
U32 LastFrameSerialID;
F32 quadTextureX;
F32 quadTextureY;
F32 blendColor;
};Change your vertexbuffer definition to look like:
// Define a vertex
DEFINE_VERT( GFXVertexFoliage,
GFXVertexFlagXYZ | GFXVertexFlagNormal | GFXVertexFlagTextureCount2 |
GFXVertexFlagUV0 | GFXVertexFlagUV1 | GFXVertexFlagTextureCount1 |
GFXVertexFlagUV0 | GFXVertexFlagUV1)
{
Point3F point;
Point3F normal;
Point2F texCoord;
Point2F texCoord2;
Point2F texCoord3;
};Add these fields to the tagfieldata:
bool mUseClustering; // flag for using clusters
U32 mCountPerCluster; // count per cluster of foliage
U32 mClusCountVariation; // variation (plus or minus) in the count per cluster
U32 mClusterRadius; // radius of a typical cluster
U32 mClusRadVariation; // variation in the radius of a cluster
U32 mInnerRadiusX;
U32 mInnerRadiusY;
U32 mOuterRadiusX;
U32 mOuterRadiusY;
StringTableEntry mGradientFile;
GFXTexHandle mGradientTexture;
bool mQuadFoliage;
F32 mBlendStart;
F32 mBlendEnd;
bool mUseBlend;and then into the tagfieldata area below it:
mGradientFile = StringTable->insert("");
mGradientTexture = GFXTexHandle();
mUseClustering = false;
mCountPerCluster = 10;
mClusCountVariation = 0;
mClusterRadius = 8;
mClusRadVariation = 2;
mInnerRadiusX = 0;
mInnerRadiusY = 0;
mOuterRadiusX = 128;
mOuterRadiusY = 128;
mQuadFoliage = false;
mBlendStart = 1.0;
mBlendEnd = 1.0;
mUseBlend = false;
#3
Add the following to you void fxFoliageReplicator::initPersistFields()
Towards the top of your void fxFoliageReplicator::CreateFoliage(void) function add:
01/18/2007 (10:40 pm)
Game/fx/fxfoliagereplicator.cppAdd the following to you void fxFoliageReplicator::initPersistFields()
addField( "GradientFile", TypeFilename, Offset( mFieldData.mGradientFile, fxFoliageReplicator ) );
addField( "QuadFoliage", TypeBool, Offset( mFieldData.mQuadFoliage, fxFoliageReplicator ) ); // Does this media have 4 images in one?
addField( "UseColorBlend", TypeBool, Offset( mFieldData.mUseBlend, fxFoliageReplicator ) ); // Does this media blend colors?
addField( "BlendStartColor", TypeF32, Offset( mFieldData.mBlendStart, fxFoliageReplicator ) );
addField( "BlendEndColor", TypeF32, Offset( mFieldData.mBlendEnd, fxFoliageReplicator ) );
addGroup( "Clustering" );
addField( "useClustering", TypeBool, Offset( mFieldData.mUseClustering, fxFoliageReplicator ) );
addField( "CountPerCluster", TypeS32, Offset( mFieldData.mCountPerCluster, fxFoliageReplicator ) );
addField( "ClusCountVariation", TypeS32, Offset( mFieldData.mClusCountVariation, fxFoliageReplicator ) );
addField( "ClusterRadius", TypeS32, Offset( mFieldData.mClusterRadius, fxFoliageReplicator ) );
addField( "ClusRadVariation", TypeS32, Offset( mFieldData.mClusRadVariation, fxFoliageReplicator ) );
endGroup( "Clustering" );Towards the top of your void fxFoliageReplicator::CreateFoliage(void) function add:
S32 countLeftInCluster; // count used to determine when a new cluster should be started S32 clusterCountMin; // minimum of the range of plants in a cluster S32 clusterCountMax; // maximum of the range of plants in a cluster U32 foliageInnerRadiusX; // inner radius of the foliage position selection U32 foliageInnerRadiusY; // inner radius of the foliage position selection U32 foliageOuterRadiusX; // outer radius of the foliage position selection U32 foliageOuterRadiusY; // outer radius of the foliage position selection U32 foliageOuterRadMinX; // outer radius X minimum U32 foliageOuterRadMaxX; // outer radius X maximum U32 foliageOuterRadMinY; // outer radius Y minimum U32 foliageOuterRadMaxY; // outer radius Y maximum Point3F clusterPosition; // position of the center of a cluster
#4
// Add Foliage.
and the
// Initialise RayCast Search Start/End Positions
to be:
01/18/2007 (10:42 pm)
Now replace everythign between the// Add Foliage.
and the
// Initialise RayCast Search Start/End Positions
to be:
// initialize the cluster stuff
clusterCountMin = mFieldData.mCountPerCluster - mFieldData.mClusCountVariation;
if ( clusterCountMin < 1 ) {
clusterCountMin = 1;
}
clusterCountMax = mFieldData.mCountPerCluster + mFieldData.mClusCountVariation;
if ( clusterCountMax < 1 ) {
clusterCountMax = 1;
}
// determine the foliage radius of variation here
if ( mFieldData.mUseClustering ) {
// plants will be placed around the cluster center
foliageInnerRadiusX = 0.f;
foliageInnerRadiusY = 0.f;
foliageOuterRadMinX = mFieldData.mClusterRadius - mFieldData.mClusRadVariation;
foliageOuterRadMaxX = mFieldData.mClusterRadius + mFieldData.mClusRadVariation;
foliageOuterRadMinY = mFieldData.mClusterRadius - mFieldData.mClusRadVariation;
foliageOuterRadMaxY = mFieldData.mClusterRadius + mFieldData.mClusRadVariation;
} else {
// plants will be arranged individually
foliageInnerRadiusX = mFieldData.mInnerRadiusX;
foliageInnerRadiusY = mFieldData.mInnerRadiusY;
foliageOuterRadiusX = mFieldData.mOuterRadiusX;
foliageOuterRadiusY = mFieldData.mOuterRadiusY;
}
// force the first cluster to be set up
countLeftInCluster = 0;
for (U32 idx = 0; idx < mFieldData.mFoliageCount; idx++)
{
fxFoliageItem* pFoliageItem;
Point3F FoliageOffsetPos;
// Reset Relocation Retry.
RelocationRetry = mFieldData.mFoliageRetries;
// Find it a home ...
do
{
// subtract from the count in cluster right here so that any failures/collisions will eventually get a new cluster
// position set
if ( mFieldData.mUseClustering ) {
countLeftInCluster--;
}
// Get the fxFoliageReplicator Position.
if (mFieldData.mUseClustering && countLeftInCluster <= 0)
{
clusterPosition = getPosition();
countLeftInCluster = RandomGen.randI( clusterCountMin, clusterCountMax );
HypX = RandomGen.randF((F32) mFieldData.mInnerRadiusX, (F32) mFieldData.mOuterRadiusX);
HypY = RandomGen.randF((F32) mFieldData.mInnerRadiusY, (F32) mFieldData.mOuterRadiusY);
Angle = RandomGen.randF(0, (F32) M_2PI);
clusterPosition.x += HypX * mCos(Angle);
clusterPosition.y += HypY * mSin(Angle);
foliageOuterRadiusX = RandomGen.randF( foliageOuterRadMinX, foliageOuterRadMaxX );
foliageOuterRadiusY = RandomGen.randF( foliageOuterRadMinY, foliageOuterRadMaxY );
}
// Get the fxFoliageReplicator Position.
if (mFieldData.mUseClustering)
FoliagePosition = clusterPosition;
else
FoliagePosition = getPosition();
// Calculate a random offset
HypX = RandomGen.randF(foliageInnerRadiusX, foliageOuterRadiusX);
HypY = RandomGen.randF(foliageInnerRadiusY, foliageOuterRadiusY);
Angle = RandomGen.randF(0, (F32) M_2PI);
// Calcualte the new position.
FoliagePosition.x += HypX * mCos(Angle);
FoliagePosition.y += HypY * mSin(Angle);
#5
// No, so turn-off flipping.
pFoliageItem->Flipped = false;
and add the following block beneath them:
In the SetUpBuffers function change this block to look like this:
and change the code in between the
// Handle texture coordinates
and the
// Handle lighting, lighting happens at the same time as global so this is just an offset
to look like this:
01/18/2007 (10:47 pm)
Next find these lines:// No, so turn-off flipping.
pFoliageItem->Flipped = false;
and add the following block beneath them:
// J.C. this allows us to put more than one texture in single image
pFoliageItem->quadTextureX = 0.0;
pFoliageItem->quadTextureY = 0.0;
// Does this texture have 4 images in one?
if (mFieldData.mQuadFoliage)
{
int x = RandomGen.randI(0,1);
if (x)
pFoliageItem->quadTextureX = 0.5;
x = RandomGen.randI(0,1);
if (x)
pFoliageItem->quadTextureY = 0.5;
}
// Are we using color blending?
if (mFieldData.mUseBlend)
{
F32 blendColor = RandomGen.randF(mFieldData.mBlendStart,mFieldData.mBlendEnd);
pFoliageItem->blendColor=(blendColor);
}
else
{
pFoliageItem->blendColor = 1.0f;
}
// End J.C.In the SetUpBuffers function change this block to look like this:
Point2F texCoords[4]; texCoords[0] = Point2F(0.0, 0.0); texCoords[1] = Point2F(0.0, 0.5); texCoords[2] = Point2F(0.5, 0.5); texCoords[3] = Point2F(0.5, 0.0);
and change the code in between the
// Handle texture coordinates
and the
// Handle lighting, lighting happens at the same time as global so this is just an offset
to look like this:
vert->texCoord = texCoords[vertIndex];
if (pFoliageItem->Flipped)
vert->texCoord.x = 1.0f - vert->texCoord.x;
vert->texCoord.x = vert->texCoord.x + pFoliageItem->quadTextureX; // J.C. add our x/y offset for quad foliages
vert->texCoord.y = vert->texCoord.y + pFoliageItem->quadTextureY;
// Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier,
// the y coordinate determines if this vertex actually sways or not.
if ((vertIndex == 0) || (vertIndex == 3)) {
vert->texCoord2.set(pFoliageItem->SwayTimeRatio / mGlobalSwayTimeRatio, 1.0f);
} else {
vert->texCoord2.set(0.0f, 0.0f);
}
vert->texCoord3.x = 0.0f;
vert->texCoord3.y = pFoliageItem->blendColor,pFoliageItem->blendColor;
#6
and underneath the // Remove texture
which isn't far below it add:
Under the // Set up our texture and color ops.
add:
At the end of your packupdate writes add:
and in the unpackupdate reads add:
01/18/2007 (10:54 pm)
Underneath the // Yes, so load foliage texture. add:mFieldData.mGradientTexture = GFXTexHandle( mFieldData.mGradientFile, &GFXDefaultStaticDiffuseProfile );
if ((GFXTextureObject*) mFieldData.mGradientTexture == NULL)
Con::printf("fxFoliageReplicator: %s is an invalid or missing foliage gradient file.", mFieldData.mFoliageFile);and underneath the // Remove texture
which isn't far below it add:
mFieldData.mGradientTexture = NULL;
Under the // Set up our texture and color ops.
add:
GFX->setTexture(2, mFieldData.mGradientTexture); GFX->setTextureStageColorOp(2, GFXTOPModulate);
At the end of your packupdate writes add:
stream->writeString(mFieldData.mGradientFile); stream->writeFlag(mFieldData.mQuadFoliage); // Quad Foliage on a Texture? stream->writeFlag(mFieldData.mUseBlend); // Use Color Blending? stream->writeFlag(mFieldData.mUseClustering ); // Foliage Use Clusters stream->write( mFieldData.mCountPerCluster ); // Foliage Count per Cluster stream->write( mFieldData.mClusCountVariation ); // Foliage Cluster Count Variation stream->write( mFieldData.mClusterRadius ); // Foliage Cluster Radius stream->write( mFieldData.mClusRadVariation ); stream->write(mFieldData.mBlendStart); stream->write(mFieldData.mBlendEnd);
and in the unpackupdate reads add:
mFieldData.mGradientFile = stream->readSTString(); mFieldData.mQuadFoliage = stream->readFlag(); // Is this a quad texture? mFieldData.mUseBlend = stream->readFlag(); // Use Color Blending mFieldData.mUseClustering = stream->readFlag(); // Foliage Use Clusters stream->read( &mFieldData.mCountPerCluster ); // Foliage Count per Cluster stream->read( &mFieldData.mClusCountVariation ); // Foliage Cluster Count Variation stream->read( &mFieldData.mClusterRadius ); // Foliage Cluster Radius stream->read( &mFieldData.mClusRadVariation ); stream->read(&mFieldData.mBlendStart); stream->read(&mFieldData.mBlendEnd);
#7
add:
One note on this... Even if you decide not to use the gradient textures, because of the way the shader is set up you will need to add a dummy gradient texture. you can just create a 2x2 jpg that is all white and it will not affect your appearance at all.
Hopefully this is useful for someone. I would have submitted it as a resource but wasn't sure how to do it. And as always, the disclaimer: I think this is 100% working, there may be some bugs in it though, either in the paste to forum or in my code that I hadn't noticed previously.
One other area that I'll probably add at some point, would be to have an option to make the color variations on a per cluster basis, it would be easy to do, though you wouldn't want it in a lot of cases. But if you were using dense grass it would be useful as you could make the variations be patchy, patches of live and dead grass, and easy to add.
01/18/2007 (10:57 pm)
In the same routine underneath the // Load Foliage Texture on the client.add:
mFieldData.mGradientTexture = GFXTexHandle( mFieldData.mGradientFile, &GFXDefaultStaticDiffuseProfile );
if ((GFXTextureObject*) mFieldData.mGradientTexture == NULL)
Con::printf("fxFoliageReplicator: %s is an invalid or missing gradient texture file.", mFieldData.mGradientFile);One note on this... Even if you decide not to use the gradient textures, because of the way the shader is set up you will need to add a dummy gradient texture. you can just create a 2x2 jpg that is all white and it will not affect your appearance at all.
Hopefully this is useful for someone. I would have submitted it as a resource but wasn't sure how to do it. And as always, the disclaimer: I think this is 100% working, there may be some bugs in it though, either in the paste to forum or in my code that I hadn't noticed previously.
One other area that I'll probably add at some point, would be to have an option to make the color variations on a per cluster basis, it would be easy to do, though you wouldn't want it in a lot of cases. But if you were using dense grass it would be useful as you could make the variations be patchy, patches of live and dead grass, and easy to add.
#8
01/19/2007 (5:31 am)
One thing you might like to add, is instead of a gradient texture, have a noise texture that alters the colour based on a noise pattern. You can then sample the U,V of the noise texture for your colour variations.
#9
01/19/2007 (11:48 am)
This should be officially added into TGEA, since it does the same as the original, but a lot more useful stuff too!
#10
I assume that clustering is optional?
01/19/2007 (7:07 pm)
Sweetness. I did the same thing for multiple textures (UV offsets and a single texture) but none of the other fancy stuff. Definitely going to swap this in there.I assume that clustering is optional?
#11
01/19/2007 (8:18 pm)
Clustering is optional, there is a UseClustering flag that you can check or uncheck in the mission editor.
#12
Has anyone else added it successfully.
I've triple checked and I'm getting reddish foliage (which should be green) which could be a byproduct that this seems to have broken the lighting part of the replicator, and it's always using the top left quadrant when not in quad mode (which means it cuts a single foliage image into a part).
01/27/2007 (1:00 pm)
Are you sure this works correctly? Has anyone else added it successfully.
I've triple checked and I'm getting reddish foliage (which should be green) which could be a byproduct that this seems to have broken the lighting part of the replicator, and it's always using the top left quadrant when not in quad mode (which means it cuts a single foliage image into a part).
#13
01/28/2007 (12:04 am)
Hmm I know the code works, but it is possible I left something out when cutting and pasting this in. I can email you the cpp and h files if you like to make things easier.
#14
I was curious how hard it would be/is to add in support to duplicate the foliage quad and rotate it 90 degrees. So instead of a single face of foliage, you'd have 2 faces that intersect at the middle (technique used in most games)..
01/28/2007 (5:52 pm)
It all seems to be working fine for us.. although still figuring out good values for various things..I was curious how hard it would be/is to add in support to duplicate the foliage quad and rotate it 90 degrees. So instead of a single face of foliage, you'd have 2 faces that intersect at the middle (technique used in most games)..
#15
01/28/2007 (6:45 pm)
Shouldn't be hard at all. I don't have time to play around with it at the moment though, run MMOG web sites for a living and Vanguard came out 2 days ago, so scrambling with all the early release stuff. Be a week or two before I have any programming time.
#16
and what site? :p
01/28/2007 (9:18 pm)
Mm Vanguard :) Played Beta of it for awhile, probably should pick up retail eventuallyand what site? :p
#17
01/29/2007 (12:55 am)
I run the databases at MMODB.com currently, which is an upstart network. In the past I ran the databases at OGaming and going way back at Gamesnet/EQ'Lizer if you want to go way back.
#18
01/29/2007 (1:41 am)
And you do this for a living? weird...
#19
01/29/2007 (3:45 am)
Ahh it's a nice gig, been doing it for nearly ten years now. Started off running normal game related web sites, but MMOG databases are great because the games last a long time, and users check in daily, and typically hit a lot of pages as they are searching for information. One of the few non-subscription ways to make money in online gaming since the ad market collapsed. Best thing about it though is that it allows me to set my own hours, and own living location since I do all my work from home. So I get to live in Thailand, and use all that money I save on game development.
#20
Nice changes though, I really like how it looks in TGEA
01/29/2007 (6:04 am)
J.C. In the changes I originally made, I went with the four textures instead of the quad, so if you want more than 4 types you could just change the constant and have as many more as you wanted. Nice changes though, I really like how it looks in TGEA
Torque 3D Owner J.C. Smith
//----------------------------------------------------------------------------- // Structures //----------------------------------------------------------------------------- struct VertData { float2 texCoord : TEXCOORD0; float2 waveScale : TEXCOORD1; float3 normal : NORMAL; float4 position : POSITION; float2 blendColor : TEXCOORD2; }; struct ConnectData { float4 hpos : POSITION; float2 outTexCoord : TEXCOORD0; float4 color : COLOR0; float4 groundAlphaCoeff : COLOR1; float2 alphaLookup : TEXCOORD1; float2 outBlendColor : TEXCOORD2; }; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- ConnectData main( VertData IN, uniform float4x4 projection : register(C0), uniform float4x4 world : register(C4), uniform float GlobalSwayPhase : register(C8), uniform float SwayMagnitudeSide : register(C9), uniform float SwayMagnitudeFront : register(C10), uniform float GlobalLightPhase : register(C11), uniform float LuminanceMagnitude : register(C12), uniform float LuminanceMidpoint : register(C13), uniform float DistanceRange : register(C14), uniform float3 CameraPos : register(C15) ) { ConnectData OUT; // Init a transform matrix to be used in the following steps float4x4 trans = 0; trans[0][0] = 1; trans[1][1] = 1; trans[2][2] = 1; trans[3][3] = 1; trans[0][3] = IN.position.x; trans[1][3] = IN.position.y; trans[2][3] = IN.position.z; // Billboard transform * world matrix float4x4 o = world; o = mul(o, trans); // Keep only the up axis result and position transform. // This gives us "cheating" cylindrical billboarding. o[0][0] = 1; o[1][0] = 0; o[2][0] = 0; o[3][0] = 0; o[0][1] = 0; o[1][1] = 1; o[2][1] = 0; o[3][1] = 0; // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, // the y coordinate determines if this vertex actually sways or not. float xSway, ySway; float wavePhase = GlobalSwayPhase * IN.waveScale.x; sincos(wavePhase, ySway, xSway); xSway = xSway * IN.waveScale.y * SwayMagnitudeSide; ySway = ySway * IN.waveScale.y * SwayMagnitudeFront; float4 p; p = mul(o, float4(IN.normal.x + xSway, ySway, IN.normal.z, 1)); // Project the point OUT.hpos = mul(projection, p); // Lighting float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + IN.normal.y); // Alpha float3 worldPos = float3(IN.position.x, IN.position.y, IN.position.z); float alpha = abs(distance(worldPos, CameraPos)) / DistanceRange; alpha = clamp(alpha, 0.0f, 1.0f); //pass it through OUT.alphaLookup = float2(alpha, 0.0f); OUT.groundAlphaCoeff = all(IN.normal.z); OUT.outTexCoord = IN.texCoord; OUT.color = float4(Luminance, Luminance, Luminance, 1.0f); OUT.outBlendColor = IN.blendColor; return OUT; }