How can I create a simple shader where I can set a value from the CPU?

tom.mclean
Fri, 10 May 2024 18:52:47 +0000
Programming & Scripting

I have made a material for ocean waves where I have a custom block which calculates the wave height and normal based on the sum of sines calculation (It has to be sum of sines, not gerstner, as I have a separate physics engine that relies on the sum of sines which cannot be changed, and I am just using unreal to visualise the physics engine). The custom block code looks like:

int numWaves = MaterialCollection0.Vectors[0][0];
float sum = 0.0f;
Normal = float3(0.0f,0.0f,0.0f);
int numParams = 5;
for (int i=0; i<36; i++)
{
	//int i=1;

	int wave = i;
	float offset = 1.0 + (wave *numParams);
	float a = MaterialCollection0.Vectors[(offset + 0)* 0.25][(offset + 0)%4];
	float omega = MaterialCollection0.Vectors[(offset + 1)* 0.25][(offset + 1)%4];
	float k1 = MaterialCollection0.Vectors[(offset + 2)* 0.25][(offset + 2)%4];
	float k2 = MaterialCollection0.Vectors[(offset + 3)* 0.25][(offset + 3)%4];
	float phase = MaterialCollection0.Vectors[(offset + 4)* 0.25][(offset + 4)%4];
	float theta = omega * Time + k1 * PosX + k2 * PosY + phase;
	float z = a * (cos(theta) + 0.2f * cos(2.0f * theta));
	sum += z;
	// Add derivative
	float msinc = -sin(theta);
	float dx = a*k1 * msinc;
	float dy = a*k2 * msinc;
	Normal += float3(-dx, dy, 0.0);
   	// --- Add secondary wave component ---
	float theta2 = 2.0*theta;
	float a2 = a*0.2f;
	float msinc2 = -sin(theta2);
	float dx2 = a2*k1 * msinc2;
	float dy2 = a2*k2 * msinc2;
	Normal += float3(-dx2, dy2, 0.0);
}

Normal.z = 1.0;
return sum;

I have got this to work by using a material collection where I set variables, however the material collection can only be a vector of length 4 vectors, whereas in my calculation the waves are defined with 5 parameters.

I want to try and make my own custom shader where I have a buffer with a struct like:

struct WaveParameter {
  float a;
  float omega;
  float k1;
  float k2;
  float phase;
}

So that I can get around the weird indexing of the material collection, to hopefully try and get some performance improvement.

I have managed to get some knowledge of shaders in unreal following these tutorials:

https://itscai.us/blog/post/ue-view-extensions/#step-1-setting-up-your-unreal-project

https://dev.epicgames.com/documentation/en-us/unreal-engine/creating-a-new-global-shader-as-a-plugin-in-unreal-engine

and

https://dev.epicgames.com/community/learning/tutorials/WkwJ/unreal-engine-simple-compute-shader-with-cpu-readback

However none of these completely do what I want to do (and the last one, I am not sure how to do the last stage).

To learn how to do what I want to do, I want to make a simple shader like:

float4 color;

float4 GetColor()
{
  return color;
}

And then just be able to set the value of the color during the games run time.

I think I need to do this eventually by doing something like:

class FOO_API FSomeShaderPS : public FGlobalShader {
  DECLARE_GLOBAL_SHADER(FSomeShaderPS);
  FSomeShaderPS () {}
  FSomeShaderPS (const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) {
    MyColorParameter.Bind(Initializer.ParameterMap, TEXT("MyColor"), SPF_Mandatory);
  }
  template<typename TShaderRHIParamRef>
  static void SetColor(FRHICommandListImmediate& RHICmdList, const TShaderRHIParamRef ShaderRHI, FLinearColor Color) {
    SetShaderValue(RHICmdList, ShaderRHI, MyColorParameter, Color);
  }
private:
  LAYOUT_FIELD(FShaderParameter, MyColorParameter);
}

And then be able to call SetColor somehow.

Am I anywhere close to correct? Are there any other good tutorials where I can learn to be able to do this?

Any help would be appreciated. Thanks