Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Custom Shader
#1
I am looking for a way to colour a mesh based on height and I saw that the HeightMap has this functionality. Unfortunately, I have a area that is not square and the triangulation is already defined therefore I cannot make use of the heightmap. Also, the heightmap caused RAM to run out very quickly on larger areas.

I also tried using the VertexColorMaterial to achieve the same effect but there is obviously colour blending across a triangle and I want a hard edge where the edge represents a single height value (like a topographical contour).

I was looking at creating a custom shader to allow shading of the mesh based on height with options to customize the height ranges (and possibly colours).

I looked through the API reference and started experimenting with custom shaders. I was able to create a SPV shader using the Vulkan SDK and I was able to load it with the shader manager (see code below). Where I am having issues is the next step. How do I go about applying the shader to a material?

I have only worked with shaders through engines like Unity and Godot where they have a UI for applying shaders to materials, so I have a lack of knowledge on how it happens at a code level.

If you could steer me in the right direction I would appreciate it.

Code:
var byteArrayVert = File.ReadAllBytes(@"C:\Users\Kyle\Desktop\shader\vert.spv");
var byteArrayFrag = File.ReadAllBytes(@"C:\Users\Kyle\Desktop\shader\frag.spv");

var sman = view!.GpuDevice!.ShadersManager;

sman.RegisterShaderResource("vs", byteArrayVert);
sman.RegisterShaderResource("fs", byteArrayFrag);

var siv = sman.GetOrCreatePipelineShaderStage("vs", ShaderStageFlags.Vertex, "main");
var sif = sman.GetOrCreatePipelineShaderStage("fs", ShaderStageFlags.Fragment, "main");
#2
First I need to say that using Vulkan is much more complicated than using DirectX (or OpenGL).

To create a custom shader you would first need to create a custom Effect (derive you effect from Effect class). This effect needs to override the ApplyRenderingItemMaterial method - there the Vulkan buffers, Pipelines and other data are set to the RenderingItem object (based on the data from the material). Also, the RenderingItem.EffectTechnique needs to be set - the EffectTechnique.Render method is called when rendering the RenderingItem in the RenderObjectsRenderingStep (FillCommandBufferRenderingStep). The EffectTechnique can be set to the StandardEffectTechnique or to your own EffectTechnique that provides your own implementation of the Render method.

If you need to provide some custom material information, then you also need to create a custom Material (derived from Material class). If you can reuse any existing material (for example StandardMaterial, VertexColorMaterial, etc.) then you only need to set the Material.Effect property to your custom effect.

This is a very short description on how to implement your own effect. As you may see, this may be quite complicated. So I would currently not recommend you to try to create your own effect.

Because all work is currently devoted to preparing the first production ready version, I do not have time to provide a sample and a document that would describe how to prepare a custom effect. This will surely be done sometime in the future.


What is more, if you want to efficiently render large height maps, then this cannot be done by just a simple custom effect. You would need to define an effect that would generate the mesh on the GPU. And this would make this even more complicated.


So I would recommend you to try to use what is available:

To render a height map with a hard gradient, you can use a one dimensional gradient texture (the color at position 0 defines the color for min value; the last position defines the color for max value).

You can create such texture by calling TextureFactory.CreateGradientTexture, for example:
Code:
var gradientData = new GradientStop[]
{
    new GradientStop(Colors.White, 1.0f),
    new GradientStop(Colors.White, 0.8f),
    new GradientStop(Colors.LightGreen, 0.799f),
    new GradientStop(Colors.LightGreen, 0.3f),
    new GradientStop(Colors.Blue, 0.299f),
    new GradientStop(Colors.Blue, 0.0f)
};

// Create 1D texture; "linear" texture that requires special texture coordinates
var gradientTexture = TextureFactory.CreateGradientTexture(GpuDevice, gradientData, textureWidth: 256);

To create hard gradient define sharp gradient stops - see AdvancedHeightMapSample.

In the next beta version it will also be possible to disable color interpolation by using the new ClampNoInterpolation sampler type (this can then be used instead of Clamp sampler that is passed to the StandardMaterial constructor).


Then the texture coordinates are defined to point to the correct color for the height value at the specific position, for example texture coordinate (0, 0.5) is set the minimum height value and texture coordinate (1, 0.5) is set to the maximum height value.

When using the height map objects from SharpEngine, you can define the mesh with texture coordinates as height values by calling constructor of HeightMapSurfaceNode and setting the useHeightValuesAsTextureCoordinates to true.

But if you do not want to use HeightMapSurfaceNode, then you can also update your mesh by setting the texture coordinates so that they will be based on the height data - from (0, 0.5) to (1, 0.5) as described before.
Andrej Benedik
#3
Thanks a lot for the very detailed reply!

For now I will try to implement texture mapping on my mesh as you suggested at the end of your reply and I look forward to a future tutorial on creating a custom shader.
  


Forum Jump:


Users browsing this thread:
1 Guest(s)