I need help with tansparency surface for ocean using hlsl and glsl

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

Hi everyone, I am trying to add oceans to my game. I can only load HLSL files instead of both HLSL and GLSL files at the same time because I am using Ogre3d 1.7 source code. I want to ask if there is a way to make the ocean surface transparent so that we can see the bottom of the ocean or the animals below the ocean surface. And is it possible to combine the structure of hlsl and glsl into the same hlsl structure file?

Below is the hlsl file structure that I am using to load the Vertex shader and pixel shader. I'm not good with the structure of the hlsl file so hopefully someone with experience can help me get transparency for the ocean surface as far as I know the pixel shader will do this, but I don't know how to rewrite or add something to be able to do this.

This the Vertex shader

Code: Select all

struct a2v {
	float4 Position : POSITION;   // in object space
	float2 TexCoord : TEXCOORD0;
};

struct v2f {
	float4 Position  : POSITION;  // in clip space
	float3 rotMatrix1 : TEXCOORD0; // first row of the 3x3 transform from tangent to obj space
	float3 rotMatrix2 : TEXCOORD1; // second row of the 3x3 transform from tangent to obj space
	float3 rotMatrix3 : TEXCOORD2; // third row of the 3x3 transform from tangent to obj space

float2 bumpCoord0 : TEXCOORD3;
float2 bumpCoord1 : TEXCOORD4;
float2 bumpCoord2 : TEXCOORD5;

float3 eyeVector  : TEXCOORD6;
};

// wave functions

struct Wave {
  float freq;  // 2*PI / wavelength
  float amp;   // amplitude
  float phase; // speed * 2*PI / wavelength
  float2 dir;
};

v2f main(a2v IN,
		uniform float4x4 WorldViewProj,
		uniform float3 eyePosition,
		uniform float BumpScale,
		uniform float2 textureScale,
		uniform float2 bumpSpeed,
		uniform float time,
		uniform float waveFreq,
		uniform float waveAmp
        )
{
	v2f OUT;

#define NWAVES 2
Wave wave[NWAVES] = {
	{ 1.0, 1.0, 0.5, float2(-1, 0) },
	{ 2.0, 0.5, 1.7, float2(-0.7, 0.7) }
};

wave[0].freq = waveFreq;
wave[0].amp = waveAmp;

wave[1].freq = waveFreq * 3.0;
wave[1].amp = waveAmp * 0.33;

float4 P = IN.Position;

// sum waves
float ddx = 0.0, ddy = 0.0;
float deriv;
float angle;

// wave synthesis using two sine waves at different frequencies and phase shift
for(int i = 0; i<NWAVES; ++i)
{
	angle = dot(wave[i].dir, P.xz) * wave[i].freq + time * wave[i].phase;
	P.y += wave[i].amp * sin( angle );
	// calculate derivate of wave function
	deriv = wave[i].freq * wave[i].amp * cos(angle);
	ddx -= deriv * wave[i].dir.x;
	ddy -= deriv * wave[i].dir.y;
}

// compute the 3x3 transform from tangent space to object space
// first rows are the tangent and binormal scaled by the bump scale

OUT.rotMatrix1.xyz = BumpScale * normalize(float3(1, ddy, 0)); // Binormal
OUT.rotMatrix2.xyz = BumpScale * normalize(float3(0, ddx, 1)); // Tangent
OUT.rotMatrix3.xyz = normalize(float3(ddx, 1, ddy)); // Normal

OUT.Position = mul(WorldViewProj, P);

// calculate texture coordinates for normal map lookup
OUT.bumpCoord0.xy = IN.TexCoord*textureScale + time * bumpSpeed;
OUT.bumpCoord1.xy = IN.TexCoord*textureScale * 8.0 + time * bumpSpeed * 10.0;
OUT.bumpCoord2.xy = IN.TexCoord*textureScale * 4.0 + time * bumpSpeed * 8.0;

OUT.eyeVector = P.xyz - eyePosition; // eye position in vertex space
return OUT;
}

This is the pixel shader

Code: Select all


struct v2f {
	float4 Position  : POSITION;  // in clip space
	float3 rotMatrix1 : TEXCOORD0; // first row of the 3x3 transform from tangent to obj space
	float3 rotMatrix2 : TEXCOORD1; // second row of the 3x3 transform from tangent to obj space
	float3 rotMatrix3 : TEXCOORD2; // third row of the 3x3 transform from tangent to obj space

float2 bumpCoord0 : TEXCOORD3;
float2 bumpCoord1 : TEXCOORD4;
float2 bumpCoord2 : TEXCOORD5;

float3 eyeVector  : TEXCOORD6;
};


float4 main(v2f IN,
			uniform sampler2D NormalMap,
			uniform samplerCUBE EnvironmentMap,
			uniform float4 deepColor,
			uniform float4 shallowColor,
			uniform float4 reflectionColor,
			uniform float reflectionAmount,
			uniform float reflectionBlur,
			uniform float waterAmount,
			uniform float fresnelPower,
			uniform float fresnelBias,
			uniform float hdrMultiplier
			) : COLOR
{
	// sum normal maps
	// sample from 3 different points so no texture repetition is noticeable
    float4 t0 = tex2D(NormalMap, IN.bumpCoord0) * 2.0 - 1.0;
    float4 t1 = tex2D(NormalMap, IN.bumpCoord1) * 2.0 - 1.0;
    float4 t2 = tex2D(NormalMap, IN.bumpCoord2) * 2.0 - 1.0;
    float3 N = t0.xyz + t1.xyz + t2.xyz;

float3x3 m; // tangent to world matrix
m[0] = IN.rotMatrix1;
m[1] = IN.rotMatrix2;
m[2] = IN.rotMatrix3;

N = normalize( mul( N, m ) );

// reflection
float3 E = normalize(IN.eyeVector);
float4 R;
R.xyz = reflect(E, N);
// Ogre conversion for cube map lookup
R.z = -R.z;
R.w = reflectionBlur;
float4 reflection = texCUBEbias(EnvironmentMap, R);
// cheap hdr effect
reflection.rgb *= (reflection.r + reflection.g + reflection.b) * hdrMultiplier;

// fresnel
float facing = 1.0 - max(dot(-E, N), 0);
float fresnel = saturate(fresnelBias + pow(facing, fresnelPower));

float4 waterColor = lerp(shallowColor, deepColor, facing) * waterAmount;

reflection = lerp(waterColor,  reflection * reflectionColor, fresnel) * reflectionAmount;
return waterColor + reflection;
}

This is the material structure that I loaded the ocean

Code: Select all

vertex_program Ogre/Ocean2VS hlsl 
{
    source Ocean2VS.hlsl
    entry_point main
    target vs_3_0
	
default_params
{
	param_named_auto WorldViewProj worldviewproj_matrix
}

}

fragment_program HLSL4/Ocean2FS hlsl
{
	source Ocean2FS.hlsl
   	entry_point main
    	target vs_3_0
    	
	default_params
{
	
}
}


material Ocean2_HLSL
{
	technique
	{
		pass
		{
			vertex_program_ref Ogre/Ocean2VS
			{
				param_named_auto eyePosition camera_position_object_space
				param_named BumpScale float 0.2
				param_named textureScale float2 25 26
				param_named bumpSpeed float2 0.015 0.005
				param_named_auto time time_0_x 100.0
				param_named waveFreq float 0.028
				param_named waveAmp float 1.8
			}

		fragment_program_ref HLSL4/Ocean2FS
		{
			param_named deepColor float4 0 0.3 0.5 1.0
			param_named shallowColor float4 0 1 1 1.0
			param_named reflectionColor float4 0.95 1 1 1.0
			param_named reflectionAmount float 1.0
			param_named reflectionBlur float 0.0
			param_named waterAmount float 0.3
			param_named fresnelPower float 5.0
			param_named fresnelBias float 0.328
			param_named hdrMultiplier float 0.471
		}

		texture_unit
		{
			texture waves2.dds
			tex_coord_set 0
			filtering linear linear linear
		}

		texture_unit
		{
			texture morning.jpg cubic
			tex_address_mode clamp
			filtering linear linear linear
			tex_coord_set 1
		}

	}

}
}
rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

Code: Select all

I can only load HLSL files instead of both HLSL and GLSL files at the same time because I am using Ogre3d 1.7 source code.

This is always the case since HLSL is for Direct3D and GLSL is for OpenGL. It is impossible to use both at the same time.
But you may have used something like CG in the past which compiles both for both platforms, but that is very outdated these days of course.

First of all, your "HLSL4/Ocean2FS hlsl" shader material is incorrect, you are using "target vs_3_0" there, which is a vertex program, which is incorrect for a fragment program. You should instead use "ps_3_0".

Secondly, the code looks a bit weird when you return the color of the water.
There are 4 channels, Red, Green, Blue and Alpha, but you seem to add them all together (at "waterColor + reflection").
This means that the Alpha channel gets at least a value of 2, which is very strange.
I would instead remake one of the variables ("reflection" or "waterColor") into float3 instead of float4, since they seem to have nothing to do with alpha either way.

Then I would make sure to set the correct alpha value to what you want, for example if both "waterColor" and "reflection" would be float3:

Code: Select all

return float4(waterColor + reflection, alpha);

What you get the alpha from may be from a simple noise texture or just a constant value, maybe even from your waves2.dds file, I am not sure.

You would also have to add this to the materials pass to be able to even use the alpha you set:

Code: Select all

scene_blend alpha_blend
depth_write off

You would also have to understand how alpha sorting and stuff works, because putting alpha blending on an entire ocean can have many negative effects and can make it so other alpha blending materials (like particles) over and under the surface may render incorrectly if it is done in an incorrect manner, so you would have to test that as well.
That is why many people these days instead use "discard;" in the fragment shader to completely skip a pixel or not (alpha rejection), since that has no issues with sorting at all, but that is of course not really possible when doing it on an ocean.

You may have to manually sort the ocean compared to the other objects, since the sorting algorithm may fail completely. If that is the case, you would have to sort the ocean with its Y value from the camera instead of any of its positions (if the ocean is at the same Y value everywhere).
It is a complex issue.

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

thanks for the help, I was able to add in the alpha channel and make the water surface transparent. But there is one more problem that I want to ask in the video below as you can see, there will be refractions and reflections from the surrounding objects and the reflection on the water will also ripple with the water's surface. I think it's called the DUDV map, can you help me with how can I add a DUDV map so that reflections on water as well as objects underwater will interact when the water is rippling?

rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

That is a much larger task, and it is impossible for me to just add it to you like that.

There is an Ogre sample of it though, it is called "Fresnel" and it uses fresnel on water with reflection and refraction exactly like you want.

User avatar
sercero
Bronze Sponsor
Bronze Sponsor
Posts: 479
Joined: Sun Jan 18, 2015 4:20 pm
Location: Buenos Aires, Argentina
x 167

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by sercero »

It is not the DUDV map that gives you the reflection, but only the displacement effect of the waves.

As @rpgplayerrobin says, the reflection is a much harder issue and the example is in the "Fresnel" demo.

You have to create a second camera to render the scene from under the water and render the output of that camera on the surface of the pond to get the reflection.

Here is the whole series from ThinMatrix: https://www.youtube.com/watch?v=HusvGeE ... qhnalNTNZh

It is for Java but you can learn the principles there and apply them to OGRE.

Oh, you might also want to take a look at OGREWater: https://github.com/OGRECave/ogrewater

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

sercero wrote: Wed Apr 26, 2023 12:35 pm

It is not the DUDV map that gives you the reflection, but only the displacement effect of the waves.

As @rpgplayerrobin says, the reflection is a much harder issue and the example is in the "Fresnel" demo.

You have to create a second camera to render the scene from under the water and render the output of that camera on the surface of the pond to get the reflection.

Here is the whole series from ThinMatrix: https://www.youtube.com/watch?v=HusvGeE ... qhnalNTNZh

It is for Java but you can learn the principles there and apply them to OGRE.

Oh, you might also want to take a look at OGREWater: https://github.com/OGRECave/ogrewater

I've watched all ThinMatrix's OpenGL water series, but when I switch to ogre3d it doesn't work. I was just learning about HLSL and wanted to create a water surface like in the example video I saw from ThinMatrix. According to what you said, with org3d I can create a flat water surface with watercolors, and Fresnel to create ripples, but what about objects around the lake that are reflected on the surface of the water will have to do. As in ThinMatrix's video on the 4th video, there is a rough guide, but it's written in Java and he defines all the variables and values to load the texture himself. I just want to make HLSL so I can create a simple water surface like in the video, but the game engine I'm using ogre3d 1.7.2 and direct3d 9 is outdated and hard to update to the new ogre3d and DirectX 11 version because it is fully built and not editable. Hope anyone who has documents or who has successfully done it can give me a little more experience or specific examples so that I can create such a water surface. Thank you so much everyone for the replies.

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

rpgplayerrobin wrote: Tue Apr 25, 2023 4:45 am

That is a much larger task, and it is impossible for me to just add it to you like that.

There is an Ogre sample of it though, it is called "Fresnel" and it uses fresnel on water with reflection and refraction exactly like you want.

So how to take the image of the surrounding object and display it on the lake like in this video, do you have any examples related to ogre3d and HLSL? Because I have a game engine that has been built and cannot be fixed, and It had been used ogre3d 1.7.2 with direct3D 9 is too old, but I want to make a water surface look as realistic as possible, but couldn't find any solution, and couldn't upgrade the version in my game engine, so I'm hoping for some help from anyone experienced here. Thank you for replying to me

rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

It is quite easy but more complex than you seem to understand currently.

HLSL cannot do what you want it to do. It cannot magically render other objects and the entire environment in a reflection on the water with just pure HLSL.

What @sercero says is correct, you need to create another camera and create a render target texture (RTT) that renders the scene from the perspective of the reflective surface each frame. Then you need to assign that texture to the HLSL shader and correctly handle it there.
This must be handled from outside of HLSL of course.
Then, if you want refraction as well, you will need another RTT to handle that if I remember correctly.

But as both me and @sercero said, there is a demo that is called "Fresnel" that does exactly what you mean in the Ogre source, and it might even work for Ogre 1.7.

rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

Here is the code that the fresnel demo uses (I remade them a bit to make them HLSL instead of CG):

Shader material:

Code: Select all

vertex_program FresnelRefractReflectVP hlsl
{
	source Fresnel.hlsl
	entry_point main_vp
	target vs_2_0
}
fragment_program FresnelRefractReflectFP hlsl
{
	source Fresnel.hlsl
	entry_point main_fp
	target ps_2_0
}

Material on the water:

Code: Select all

material Examples/FresnelReflectionRefraction
{
	// ps_2_0 / arbfp1
	technique
	{
		pass 
		{
			
		vertex_program_ref FresnelRefractReflectVP
		{
			param_named_auto worldViewProjMatrix worldviewproj_matrix
			param_named_auto eyePosition camera_position_object_space
			param_named_auto timeVal time 0.05
			param_named scroll float 1  
			param_named scale float 1 
			param_named noise float 1 
			// scroll and noisePos will need updating per frame
		}
		fragment_program_ref FresnelRefractReflectFP
		{
			param_named fresnelBias float -0.1 
			param_named fresnelScale float 1.8 
			param_named fresnelPower float 8  
			param_named tintColour float4 0 0.05 0.05 1
			param_named noiseScale float 0.05 
		}
		// Noise
		texture_unit
		{
			// Perlin noise volume
			texture waves2.dds
			// min / mag filtering, no mip
			filtering linear linear none
		}
		// Reflection
		texture_unit
		{
			content_type compositor Fresnel reflection
			tex_address_mode clamp
		}
		// Refraction
		texture_unit
		{
			content_type compositor Fresnel refraction
			tex_address_mode clamp
		}
	}
	
		
}
}

Shader "Fresnel.hlsl":

Code: Select all

// Vertex program for fresnel reflections / refractions
void main_vp(
		float4 pos			: POSITION,
		float4 normal		: NORMAL,
		float2 tex			: TEXCOORD0,
		
	out float4 oPos		: POSITION,
	out float3 noiseCoord : TEXCOORD0,
	out float4 projectionCoord : TEXCOORD1,
	out float3 oEyeDir : TEXCOORD2, 
	out float3 oNormal : TEXCOORD3, 

	uniform float4x4 worldViewProjMatrix,
	uniform float3 eyePosition, // object space
	uniform float timeVal,
	uniform float scale,  // the amount to scale the noise texture by
	uniform float scroll, // the amount by which to scroll the noise
	uniform float noise  // the noise perturb as a factor of the  time
	)
{
	oPos = mul(worldViewProjMatrix, pos);
	// Projective texture coordinates, adjust for mapping
	float4x4 scalemat = float4x4(0.5,   0,   0, 0.5, 
	                               0,-0.5,   0, 0.5,
								   0,   0, 0.5, 0.5,
								   0,   0,   0,   1);
	projectionCoord = mul(scalemat, oPos);
	// Noise map coords
	noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
	noiseCoord.z = noise * timeVal;

oEyeDir = normalize(pos.xyz - eyePosition); 
oNormal = normal.rgb; 
}

// Fragment program for distorting a texture using a 3D noise texture
void main_fp(
		float4 pos			: POSITION,
    float3 noiseCoord			: TEXCOORD0,
		float4 projectionCoord		: TEXCOORD1,
		float3 eyeDir				: TEXCOORD2,
		float3 normal				: TEXCOORD3,
		
	out float4 col		: COLOR,
	
	uniform float4 tintColour,
	uniform float noiseScale, 
	uniform float fresnelBias,
	uniform float fresnelScale,
	uniform float fresnelPower,
	uniform sampler2D noiseMap : register(s0),
	uniform sampler2D reflectMap : register(s1),
	uniform sampler2D refractMap : register(s2)
	)
{
	// Do the tex projection manually so we can distort _after_
	float2 final = projectionCoord.xy / projectionCoord.w;

// Noise
float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale;
final += noiseNormal.xz;

// Fresnel
//normal = normalize(normal + noiseNormal.xz);
float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);

// Reflection / refraction
float4 reflectionColour = tex2D(reflectMap, final);
float4 refractionColour = tex2D(refractMap, final) + tintColour;

// Final colour
col = lerp(refractionColour, reflectionColour, fresnel);
}

Compositor:

Code: Select all

compositor Fresnel
{
    technique
    {
        texture reflection 512 512 PF_BYTE_RGB
        texture refraction 512 512 PF_BYTE_RGB
        target reflection
        {
            visibility_mask 0x00F // SURFACE objects
            input previous
        }
        target refraction
        {
            visibility_mask 0x0F0 // SUBMERGED objects
            input previous
        }
        target_output
        {
            input previous
        }
    }
}

And here is the entire source code (just a .h file) that handles everything:

Code: Select all

#ifndef __Fresnel_H__
#define __Fresnel_H__

#include "SdkSample.h"

using namespace Ogre;
using namespace OgreBites;

class _OgreSampleClassExport Sample_Fresnel : public SdkSample, public RenderTargetListener
{
public:
    static const uint32 SUBMERGED_MASK = 0x0F0;
    static const uint32 SURFACE_MASK = 0x00F;
    static const uint32 WATER_MASK = 0xF00;

Sample_Fresnel() : NUM_FISH(30), NUM_FISH_WAYPOINTS(10), FISH_PATH_LENGTH(200), FISH_SCALE(2)
{
    mInfo["Title"] = "Fresnel";
    mInfo["Description"] = "Shows how to create reflections and refractions using render-to-texture and shaders.";
    mInfo["Thumbnail"] = "thumb_fresnel.png";
    mInfo["Category"] = "Unsorted";
}

void testCapabilities(const RenderSystemCapabilities* caps)
{
    requireMaterial("Examples/FresnelReflectionRefraction");
}

bool frameRenderingQueued(const FrameEvent &evt)
{
    // update the fish spline path animations and loop as needed
    mFishAnimTime += evt.timeSinceLastFrame;
    while (mFishAnimTime >= FISH_PATH_LENGTH) mFishAnimTime -= FISH_PATH_LENGTH;

    for (unsigned int i = 0; i < NUM_FISH; i++)
    {
        mFishAnimStates[i]->addTime(evt.timeSinceLastFrame * 2);  // update fish swim animation

        // set the new position based on the spline path and set the direction based on displacement
        Vector3 lastPos = mFishNodes[i]->getPosition();
        mFishNodes[i]->setPosition(mFishSplines[i].interpolate(mFishAnimTime / FISH_PATH_LENGTH));
        mFishNodes[i]->setDirection(mFishNodes[i]->getPosition() - lastPos, Node::TS_PARENT, Vector3::NEGATIVE_UNIT_X);
        mFishNodes[i]->setFixedYawAxis(true);
    }

    return SdkSample::frameRenderingQueued(evt);
}

void preRenderTargetUpdate(const RenderTargetEvent& evt)
{
    mCamera->enableReflection(mWaterPlane);
}

void postRenderTargetUpdate(const RenderTargetEvent& evt)
{
    mCamera->disableReflection();
}

protected:

void setupContent()
{
    mCameraNode->setPosition(-50, 125, 760);
    mCameraMan->setTopSpeed(280);

    mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));  // set ambient light

    mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");  // set a skybox

    // make the scene's main light come from above
    auto ln = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    ln->setDirection(Vector3::NEGATIVE_UNIT_Y);
    ln->attachObject(mSceneMgr->createLight(Light::LT_DIRECTIONAL));

    setupWater();
    setupProps();
    setupFish();

    for (auto e : mSurfaceEnts)
        e->setVisibilityFlags(SURFACE_MASK);
    for (auto e : mSubmergedEnts)
        e->setVisibilityFlags(SUBMERGED_MASK);
}

void setupWater()
{
    auto compositor = CompositorManager::getSingleton().addCompositor(mViewport, "Fresnel");
    CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Fresnel", true);

    // toggle reflection in camera
    compositor->getRenderTarget("reflection")->addListener(this);

    mCamera->setAutoAspectRatio(true);
    // create our water plane mesh
    mWaterPlane = Plane(Vector3::UNIT_Y, 0);
    MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        mWaterPlane, 700, 1300, 10, 10, true, 1, 3, 5, Vector3::UNIT_Z);

    // create a water entity using our mesh, give it the shader material, and attach it to the origin
    mWater = mSceneMgr->createEntity("Water", "water");
    mWater->setMaterialName("Examples/FresnelReflectionRefraction");
    mSceneMgr->getRootSceneNode()->attachObject(mWater);

    // hide the water from the render textures
    mWater->setVisibilityFlags(WATER_MASK);
}

void windowUpdate()
{
    mWindow->update();
}

void setupProps()
{
    Entity* ent;

    // setting up props might take a while, so create a progress bar for visual feedback
    ProgressBar* pb = mTrayMgr->createProgressBar(TL_CENTER, "FresnelBuildingBar", "Creating Props...", 280, 100);
    mTrayMgr->showBackdrop("SdkTrays/Shade");

    pb->setComment("Upper Bath");
    windowUpdate();
    ent = mSceneMgr->createEntity("UpperBath", "RomanBathUpper.mesh" );
    mSceneMgr->getRootSceneNode()->attachObject(ent);        
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.4);

    pb->setComment("Columns");
    windowUpdate();
    ent = mSceneMgr->createEntity("Columns", "Columns.mesh");
    mSceneMgr->getRootSceneNode()->attachObject(ent);        
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.5);

    pb->setComment("Ogre Head");
    windowUpdate();
    ent = mSceneMgr->createEntity("Head", "ogrehead.mesh");
    ent->setMaterialName("RomanBath/OgreStone");
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.6);

    SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    headNode->setPosition(-350, 55, 130);
    headNode->yaw(Degree(90));
    headNode->attachObject(ent);

    pb->setComment("Lower Bath");
    windowUpdate();
    ent = mSceneMgr->createEntity("LowerBath", "RomanBathLower.mesh");
    mSceneMgr->getRootSceneNode()->attachObject(ent);
    mSubmergedEnts.push_back(ent);
    pb->setProgress(1);
    windowUpdate();

    mTrayMgr->destroyWidget(pb);
    mTrayMgr->hideBackdrop();
}

void setupFish()
{
    mFishNodes.resize(NUM_FISH);
    mFishAnimStates.resize(NUM_FISH);
    mFishSplines.resize(NUM_FISH);

    for (unsigned int i = 0; i < NUM_FISH; i++)
    {
        // create fish entity
        Entity* ent = mSceneMgr->createEntity("Fish" + StringConverter::toString(i + 1), "fish.mesh");
        mSubmergedEnts.push_back(ent);

        // create an appropriately scaled node and attach the entity
        mFishNodes[i] = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        mFishNodes[i]->setScale(Vector3::UNIT_SCALE * FISH_SCALE);
        mFishNodes[i]->attachObject(ent);

        // enable and save the swim animation state
        mFishAnimStates[i] = ent->getAnimationState("swim");
        mFishAnimStates[i]->setEnabled(true);

        mFishSplines[i].setAutoCalculate(false);  // save the tangent calculation for when we are all done

        // generate random waypoints for the fish to swim through
        for (unsigned int j = 0; j < NUM_FISH_WAYPOINTS; j++)
        {
            Vector3 pos(Math::SymmetricRandom() * 270, -10, Math::SymmetricRandom() * 700);

            if (j > 0)  // make sure the waypoint isn't too far from the last, or our fish will be turbo-fish
            {
                const Vector3& lastPos = mFishSplines[i].getPoint(j - 1);
                Vector3 delta = pos - lastPos;
                if (delta.length() > 750) pos = lastPos + delta.normalisedCopy() * 750;
            }

            mFishSplines[i].addPoint(pos);
        }

        // close the spline and calculate all the tangents at once
        mFishSplines[i].addPoint(mFishSplines[i].getPoint(0));
        mFishSplines[i].recalcTangents();
    }

    mFishAnimTime = 0;
}

void cleanupContent()
{
    mSurfaceEnts.clear();
    mSubmergedEnts.clear();
    mFishNodes.clear();
    mFishAnimStates.clear();
    mFishSplines.clear();

    MeshManager::getSingleton().remove("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}

const unsigned int NUM_FISH;
const unsigned int NUM_FISH_WAYPOINTS;
const unsigned int FISH_PATH_LENGTH; 
const Real FISH_SCALE;
std::vector<Entity*> mSurfaceEnts;
std::vector<Entity*> mSubmergedEnts;
Plane mWaterPlane;
Entity* mWater;
std::vector<SceneNode*> mFishNodes;
std::vector<AnimationState*> mFishAnimStates;
std::vector<SimpleSpline> mFishSplines;
Real mFishAnimTime;
};

#endif

The source code basically does this:

  • Sets different visibility flags for objects over the surface. These are rendered into the reflection texture using the compositor later.

  • Sets different visibility flags for objects under the surface. These are rendered into the refraction texture using the compositor later.

  • It activates the compositor and adds its own class as a listener to it, so it can enable/disable reflection of the camera.

  • It setups a plane where the water originates, which is a simple infinite plane with a distance (Plane(Vector3::UNIT_Y, Vector3(0, 10, 0) for a water plane that points up and is 10 meters up).

  • Creates the water mesh and applies the material to it. It also sets a visibility mask so it is never rendered into any RTT, because that would be very bad if it did.

Then the compositor takes care of the rest. It renders what it needs into the two textures and it also applies those textures to the water plane material automatically.

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

rpgplayerrobin wrote: Fri Apr 28, 2023 4:42 am

Here is the code that the fresnel demo uses (I remade them a bit to make them HLSL instead of CG):

Shader material:

Code: Select all

vertex_program FresnelRefractReflectVP hlsl
{
	source Fresnel.hlsl
	entry_point main_vp
	target vs_2_0
}
fragment_program FresnelRefractReflectFP hlsl
{
	source Fresnel.hlsl
	entry_point main_fp
	target ps_2_0
}

Material on the water:

Code: Select all

material Examples/FresnelReflectionRefraction
{
	// ps_2_0 / arbfp1
	technique
	{
		pass 
		{
			
		vertex_program_ref FresnelRefractReflectVP
		{
			param_named_auto worldViewProjMatrix worldviewproj_matrix
			param_named_auto eyePosition camera_position_object_space
			param_named_auto timeVal time 0.05
			param_named scroll float 1  
			param_named scale float 1 
			param_named noise float 1 
			// scroll and noisePos will need updating per frame
		}
		fragment_program_ref FresnelRefractReflectFP
		{
			param_named fresnelBias float -0.1 
			param_named fresnelScale float 1.8 
			param_named fresnelPower float 8  
			param_named tintColour float4 0 0.05 0.05 1
			param_named noiseScale float 0.05 
		}
		// Noise
		texture_unit
		{
			// Perlin noise volume
			texture waves2.dds
			// min / mag filtering, no mip
			filtering linear linear none
		}
		// Reflection
		texture_unit
		{
			content_type compositor Fresnel reflection
			tex_address_mode clamp
		}
		// Refraction
		texture_unit
		{
			content_type compositor Fresnel refraction
			tex_address_mode clamp
		}
	}
	
		
}
}

Shader "Fresnel.hlsl":

Code: Select all

// Vertex program for fresnel reflections / refractions
void main_vp(
		float4 pos			: POSITION,
		float4 normal		: NORMAL,
		float2 tex			: TEXCOORD0,
		
	out float4 oPos		: POSITION,
	out float3 noiseCoord : TEXCOORD0,
	out float4 projectionCoord : TEXCOORD1,
	out float3 oEyeDir : TEXCOORD2, 
	out float3 oNormal : TEXCOORD3, 

	uniform float4x4 worldViewProjMatrix,
	uniform float3 eyePosition, // object space
	uniform float timeVal,
	uniform float scale,  // the amount to scale the noise texture by
	uniform float scroll, // the amount by which to scroll the noise
	uniform float noise  // the noise perturb as a factor of the  time
	)
{
	oPos = mul(worldViewProjMatrix, pos);
	// Projective texture coordinates, adjust for mapping
	float4x4 scalemat = float4x4(0.5,   0,   0, 0.5, 
	                               0,-0.5,   0, 0.5,
								   0,   0, 0.5, 0.5,
								   0,   0,   0,   1);
	projectionCoord = mul(scalemat, oPos);
	// Noise map coords
	noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
	noiseCoord.z = noise * timeVal;

oEyeDir = normalize(pos.xyz - eyePosition); 
oNormal = normal.rgb; 
}

// Fragment program for distorting a texture using a 3D noise texture
void main_fp(
		float4 pos			: POSITION,
    float3 noiseCoord			: TEXCOORD0,
		float4 projectionCoord		: TEXCOORD1,
		float3 eyeDir				: TEXCOORD2,
		float3 normal				: TEXCOORD3,
		
	out float4 col		: COLOR,
	
	uniform float4 tintColour,
	uniform float noiseScale, 
	uniform float fresnelBias,
	uniform float fresnelScale,
	uniform float fresnelPower,
	uniform sampler2D noiseMap : register(s0),
	uniform sampler2D reflectMap : register(s1),
	uniform sampler2D refractMap : register(s2)
	)
{
	// Do the tex projection manually so we can distort _after_
	float2 final = projectionCoord.xy / projectionCoord.w;

// Noise
float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale;
final += noiseNormal.xz;

// Fresnel
//normal = normalize(normal + noiseNormal.xz);
float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);

// Reflection / refraction
float4 reflectionColour = tex2D(reflectMap, final);
float4 refractionColour = tex2D(refractMap, final) + tintColour;

// Final colour
col = lerp(refractionColour, reflectionColour, fresnel);
}

Compositor:

Code: Select all

compositor Fresnel
{
    technique
    {
        texture reflection 512 512 PF_BYTE_RGB
        texture refraction 512 512 PF_BYTE_RGB
        target reflection
        {
            visibility_mask 0x00F // SURFACE objects
            input previous
        }
        target refraction
        {
            visibility_mask 0x0F0 // SUBMERGED objects
            input previous
        }
        target_output
        {
            input previous
        }
    }
}

And here is the entire source code (just a .h file) that handles everything:

Code: Select all

#ifndef __Fresnel_H__
#define __Fresnel_H__

#include "SdkSample.h"

using namespace Ogre;
using namespace OgreBites;

class _OgreSampleClassExport Sample_Fresnel : public SdkSample, public RenderTargetListener
{
public:
    static const uint32 SUBMERGED_MASK = 0x0F0;
    static const uint32 SURFACE_MASK = 0x00F;
    static const uint32 WATER_MASK = 0xF00;

Sample_Fresnel() : NUM_FISH(30), NUM_FISH_WAYPOINTS(10), FISH_PATH_LENGTH(200), FISH_SCALE(2)
{
    mInfo["Title"] = "Fresnel";
    mInfo["Description"] = "Shows how to create reflections and refractions using render-to-texture and shaders.";
    mInfo["Thumbnail"] = "thumb_fresnel.png";
    mInfo["Category"] = "Unsorted";
}

void testCapabilities(const RenderSystemCapabilities* caps)
{
    requireMaterial("Examples/FresnelReflectionRefraction");
}

bool frameRenderingQueued(const FrameEvent &evt)
{
    // update the fish spline path animations and loop as needed
    mFishAnimTime += evt.timeSinceLastFrame;
    while (mFishAnimTime >= FISH_PATH_LENGTH) mFishAnimTime -= FISH_PATH_LENGTH;

    for (unsigned int i = 0; i < NUM_FISH; i++)
    {
        mFishAnimStates[i]->addTime(evt.timeSinceLastFrame * 2);  // update fish swim animation

        // set the new position based on the spline path and set the direction based on displacement
        Vector3 lastPos = mFishNodes[i]->getPosition();
        mFishNodes[i]->setPosition(mFishSplines[i].interpolate(mFishAnimTime / FISH_PATH_LENGTH));
        mFishNodes[i]->setDirection(mFishNodes[i]->getPosition() - lastPos, Node::TS_PARENT, Vector3::NEGATIVE_UNIT_X);
        mFishNodes[i]->setFixedYawAxis(true);
    }

    return SdkSample::frameRenderingQueued(evt);
}

void preRenderTargetUpdate(const RenderTargetEvent& evt)
{
    mCamera->enableReflection(mWaterPlane);
}

void postRenderTargetUpdate(const RenderTargetEvent& evt)
{
    mCamera->disableReflection();
}

protected:

void setupContent()
{
    mCameraNode->setPosition(-50, 125, 760);
    mCameraMan->setTopSpeed(280);

    mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));  // set ambient light

    mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");  // set a skybox

    // make the scene's main light come from above
    auto ln = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    ln->setDirection(Vector3::NEGATIVE_UNIT_Y);
    ln->attachObject(mSceneMgr->createLight(Light::LT_DIRECTIONAL));

    setupWater();
    setupProps();
    setupFish();

    for (auto e : mSurfaceEnts)
        e->setVisibilityFlags(SURFACE_MASK);
    for (auto e : mSubmergedEnts)
        e->setVisibilityFlags(SUBMERGED_MASK);
}

void setupWater()
{
    auto compositor = CompositorManager::getSingleton().addCompositor(mViewport, "Fresnel");
    CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Fresnel", true);

    // toggle reflection in camera
    compositor->getRenderTarget("reflection")->addListener(this);

    mCamera->setAutoAspectRatio(true);
    // create our water plane mesh
    mWaterPlane = Plane(Vector3::UNIT_Y, 0);
    MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        mWaterPlane, 700, 1300, 10, 10, true, 1, 3, 5, Vector3::UNIT_Z);

    // create a water entity using our mesh, give it the shader material, and attach it to the origin
    mWater = mSceneMgr->createEntity("Water", "water");
    mWater->setMaterialName("Examples/FresnelReflectionRefraction");
    mSceneMgr->getRootSceneNode()->attachObject(mWater);

    // hide the water from the render textures
    mWater->setVisibilityFlags(WATER_MASK);
}

void windowUpdate()
{
    mWindow->update();
}

void setupProps()
{
    Entity* ent;

    // setting up props might take a while, so create a progress bar for visual feedback
    ProgressBar* pb = mTrayMgr->createProgressBar(TL_CENTER, "FresnelBuildingBar", "Creating Props...", 280, 100);
    mTrayMgr->showBackdrop("SdkTrays/Shade");

    pb->setComment("Upper Bath");
    windowUpdate();
    ent = mSceneMgr->createEntity("UpperBath", "RomanBathUpper.mesh" );
    mSceneMgr->getRootSceneNode()->attachObject(ent);        
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.4);

    pb->setComment("Columns");
    windowUpdate();
    ent = mSceneMgr->createEntity("Columns", "Columns.mesh");
    mSceneMgr->getRootSceneNode()->attachObject(ent);        
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.5);

    pb->setComment("Ogre Head");
    windowUpdate();
    ent = mSceneMgr->createEntity("Head", "ogrehead.mesh");
    ent->setMaterialName("RomanBath/OgreStone");
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.6);

    SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    headNode->setPosition(-350, 55, 130);
    headNode->yaw(Degree(90));
    headNode->attachObject(ent);

    pb->setComment("Lower Bath");
    windowUpdate();
    ent = mSceneMgr->createEntity("LowerBath", "RomanBathLower.mesh");
    mSceneMgr->getRootSceneNode()->attachObject(ent);
    mSubmergedEnts.push_back(ent);
    pb->setProgress(1);
    windowUpdate();

    mTrayMgr->destroyWidget(pb);
    mTrayMgr->hideBackdrop();
}

void setupFish()
{
    mFishNodes.resize(NUM_FISH);
    mFishAnimStates.resize(NUM_FISH);
    mFishSplines.resize(NUM_FISH);

    for (unsigned int i = 0; i < NUM_FISH; i++)
    {
        // create fish entity
        Entity* ent = mSceneMgr->createEntity("Fish" + StringConverter::toString(i + 1), "fish.mesh");
        mSubmergedEnts.push_back(ent);

        // create an appropriately scaled node and attach the entity
        mFishNodes[i] = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        mFishNodes[i]->setScale(Vector3::UNIT_SCALE * FISH_SCALE);
        mFishNodes[i]->attachObject(ent);

        // enable and save the swim animation state
        mFishAnimStates[i] = ent->getAnimationState("swim");
        mFishAnimStates[i]->setEnabled(true);

        mFishSplines[i].setAutoCalculate(false);  // save the tangent calculation for when we are all done

        // generate random waypoints for the fish to swim through
        for (unsigned int j = 0; j < NUM_FISH_WAYPOINTS; j++)
        {
            Vector3 pos(Math::SymmetricRandom() * 270, -10, Math::SymmetricRandom() * 700);

            if (j > 0)  // make sure the waypoint isn't too far from the last, or our fish will be turbo-fish
            {
                const Vector3& lastPos = mFishSplines[i].getPoint(j - 1);
                Vector3 delta = pos - lastPos;
                if (delta.length() > 750) pos = lastPos + delta.normalisedCopy() * 750;
            }

            mFishSplines[i].addPoint(pos);
        }

        // close the spline and calculate all the tangents at once
        mFishSplines[i].addPoint(mFishSplines[i].getPoint(0));
        mFishSplines[i].recalcTangents();
    }

    mFishAnimTime = 0;
}

void cleanupContent()
{
    mSurfaceEnts.clear();
    mSubmergedEnts.clear();
    mFishNodes.clear();
    mFishAnimStates.clear();
    mFishSplines.clear();

    MeshManager::getSingleton().remove("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}

const unsigned int NUM_FISH;
const unsigned int NUM_FISH_WAYPOINTS;
const unsigned int FISH_PATH_LENGTH; 
const Real FISH_SCALE;
std::vector<Entity*> mSurfaceEnts;
std::vector<Entity*> mSubmergedEnts;
Plane mWaterPlane;
Entity* mWater;
std::vector<SceneNode*> mFishNodes;
std::vector<AnimationState*> mFishAnimStates;
std::vector<SimpleSpline> mFishSplines;
Real mFishAnimTime;
};

#endif

The source code basically does this:

  • Sets different visibility flags for objects over the surface. These are rendered into the reflection texture using the compositor later.

  • Sets different visibility flags for objects under the surface. These are rendered into the refraction texture using the compositor later.

  • It activates the compositor and adds its own class as a listener to it, so it can enable/disable reflection of the camera.

  • It setups a plane where the water originates, which is a simple infinite plane with a distance (Plane(Vector3::UNIT_Y, Vector3(0, 10, 0) for a water plane that points up and is 10 meters up).

  • Creates the water mesh and applies the material to it. It also sets a visibility mask so it is never rendered into any RTT, because that would be very bad if it did.

Then the compositor takes care of the rest. It renders what it needs into the two textures and it also applies those textures to the water plane material automatically.

I followed your instructions and got a lovely fresco. However, one thing that doesn't work is that my game engine cannot be edited to upload to the Compositor, so my refraction is still a 2d texture and there is no depth nor transparency water to see I can see the bottom because when I take-off the refraction texture the whole water surface is black when I add the texture it's a 2d texture and covers the bottom of the water and nothing is visible below. How to make the water surface transparent and the bottom visible? Is there any way I can deal with this? Thank you so much for helping me.

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

phuvinh1408 wrote: Fri Apr 28, 2023 9:47 am
rpgplayerrobin wrote: Fri Apr 28, 2023 4:42 am

Here is the code that the fresnel demo uses (I remade them a bit to make them HLSL instead of CG):

Shader material:

Code: Select all

vertex_program FresnelRefractReflectVP hlsl
{
	source Fresnel.hlsl
	entry_point main_vp
	target vs_2_0
}
fragment_program FresnelRefractReflectFP hlsl
{
	source Fresnel.hlsl
	entry_point main_fp
	target ps_2_0
}

Material on the water:

Code: Select all

material Examples/FresnelReflectionRefraction
{
	// ps_2_0 / arbfp1
	technique
	{
		pass 
		{
			
		vertex_program_ref FresnelRefractReflectVP
		{
			param_named_auto worldViewProjMatrix worldviewproj_matrix
			param_named_auto eyePosition camera_position_object_space
			param_named_auto timeVal time 0.05
			param_named scroll float 1  
			param_named scale float 1 
			param_named noise float 1 
			// scroll and noisePos will need updating per frame
		}
		fragment_program_ref FresnelRefractReflectFP
		{
			param_named fresnelBias float -0.1 
			param_named fresnelScale float 1.8 
			param_named fresnelPower float 8  
			param_named tintColour float4 0 0.05 0.05 1
			param_named noiseScale float 0.05 
		}
		// Noise
		texture_unit
		{
			// Perlin noise volume
			texture waves2.dds
			// min / mag filtering, no mip
			filtering linear linear none
		}
		// Reflection
		texture_unit
		{
			content_type compositor Fresnel reflection
			tex_address_mode clamp
		}
		// Refraction
		texture_unit
		{
			content_type compositor Fresnel refraction
			tex_address_mode clamp
		}
	}
	
		
}
}

Shader "Fresnel.hlsl":

Code: Select all

// Vertex program for fresnel reflections / refractions
void main_vp(
		float4 pos			: POSITION,
		float4 normal		: NORMAL,
		float2 tex			: TEXCOORD0,
		
	out float4 oPos		: POSITION,
	out float3 noiseCoord : TEXCOORD0,
	out float4 projectionCoord : TEXCOORD1,
	out float3 oEyeDir : TEXCOORD2, 
	out float3 oNormal : TEXCOORD3, 

	uniform float4x4 worldViewProjMatrix,
	uniform float3 eyePosition, // object space
	uniform float timeVal,
	uniform float scale,  // the amount to scale the noise texture by
	uniform float scroll, // the amount by which to scroll the noise
	uniform float noise  // the noise perturb as a factor of the  time
	)
{
	oPos = mul(worldViewProjMatrix, pos);
	// Projective texture coordinates, adjust for mapping
	float4x4 scalemat = float4x4(0.5,   0,   0, 0.5, 
	                               0,-0.5,   0, 0.5,
								   0,   0, 0.5, 0.5,
								   0,   0,   0,   1);
	projectionCoord = mul(scalemat, oPos);
	// Noise map coords
	noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
	noiseCoord.z = noise * timeVal;

oEyeDir = normalize(pos.xyz - eyePosition); 
oNormal = normal.rgb; 
}

// Fragment program for distorting a texture using a 3D noise texture
void main_fp(
		float4 pos			: POSITION,
    float3 noiseCoord			: TEXCOORD0,
		float4 projectionCoord		: TEXCOORD1,
		float3 eyeDir				: TEXCOORD2,
		float3 normal				: TEXCOORD3,
		
	out float4 col		: COLOR,
	
	uniform float4 tintColour,
	uniform float noiseScale, 
	uniform float fresnelBias,
	uniform float fresnelScale,
	uniform float fresnelPower,
	uniform sampler2D noiseMap : register(s0),
	uniform sampler2D reflectMap : register(s1),
	uniform sampler2D refractMap : register(s2)
	)
{
	// Do the tex projection manually so we can distort _after_
	float2 final = projectionCoord.xy / projectionCoord.w;

// Noise
float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale;
final += noiseNormal.xz;

// Fresnel
//normal = normalize(normal + noiseNormal.xz);
float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);

// Reflection / refraction
float4 reflectionColour = tex2D(reflectMap, final);
float4 refractionColour = tex2D(refractMap, final) + tintColour;

// Final colour
col = lerp(refractionColour, reflectionColour, fresnel);
}

Compositor:

Code: Select all

compositor Fresnel
{
    technique
    {
        texture reflection 512 512 PF_BYTE_RGB
        texture refraction 512 512 PF_BYTE_RGB
        target reflection
        {
            visibility_mask 0x00F // SURFACE objects
            input previous
        }
        target refraction
        {
            visibility_mask 0x0F0 // SUBMERGED objects
            input previous
        }
        target_output
        {
            input previous
        }
    }
}

And here is the entire source code (just a .h file) that handles everything:

Code: Select all

#ifndef __Fresnel_H__
#define __Fresnel_H__

#include "SdkSample.h"

using namespace Ogre;
using namespace OgreBites;

class _OgreSampleClassExport Sample_Fresnel : public SdkSample, public RenderTargetListener
{
public:
    static const uint32 SUBMERGED_MASK = 0x0F0;
    static const uint32 SURFACE_MASK = 0x00F;
    static const uint32 WATER_MASK = 0xF00;

Sample_Fresnel() : NUM_FISH(30), NUM_FISH_WAYPOINTS(10), FISH_PATH_LENGTH(200), FISH_SCALE(2)
{
    mInfo["Title"] = "Fresnel";
    mInfo["Description"] = "Shows how to create reflections and refractions using render-to-texture and shaders.";
    mInfo["Thumbnail"] = "thumb_fresnel.png";
    mInfo["Category"] = "Unsorted";
}

void testCapabilities(const RenderSystemCapabilities* caps)
{
    requireMaterial("Examples/FresnelReflectionRefraction");
}

bool frameRenderingQueued(const FrameEvent &evt)
{
    // update the fish spline path animations and loop as needed
    mFishAnimTime += evt.timeSinceLastFrame;
    while (mFishAnimTime >= FISH_PATH_LENGTH) mFishAnimTime -= FISH_PATH_LENGTH;

    for (unsigned int i = 0; i < NUM_FISH; i++)
    {
        mFishAnimStates[i]->addTime(evt.timeSinceLastFrame * 2);  // update fish swim animation

        // set the new position based on the spline path and set the direction based on displacement
        Vector3 lastPos = mFishNodes[i]->getPosition();
        mFishNodes[i]->setPosition(mFishSplines[i].interpolate(mFishAnimTime / FISH_PATH_LENGTH));
        mFishNodes[i]->setDirection(mFishNodes[i]->getPosition() - lastPos, Node::TS_PARENT, Vector3::NEGATIVE_UNIT_X);
        mFishNodes[i]->setFixedYawAxis(true);
    }

    return SdkSample::frameRenderingQueued(evt);
}

void preRenderTargetUpdate(const RenderTargetEvent& evt)
{
    mCamera->enableReflection(mWaterPlane);
}

void postRenderTargetUpdate(const RenderTargetEvent& evt)
{
    mCamera->disableReflection();
}

protected:

void setupContent()
{
    mCameraNode->setPosition(-50, 125, 760);
    mCameraMan->setTopSpeed(280);

    mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));  // set ambient light

    mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");  // set a skybox

    // make the scene's main light come from above
    auto ln = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    ln->setDirection(Vector3::NEGATIVE_UNIT_Y);
    ln->attachObject(mSceneMgr->createLight(Light::LT_DIRECTIONAL));

    setupWater();
    setupProps();
    setupFish();

    for (auto e : mSurfaceEnts)
        e->setVisibilityFlags(SURFACE_MASK);
    for (auto e : mSubmergedEnts)
        e->setVisibilityFlags(SUBMERGED_MASK);
}

void setupWater()
{
    auto compositor = CompositorManager::getSingleton().addCompositor(mViewport, "Fresnel");
    CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Fresnel", true);

    // toggle reflection in camera
    compositor->getRenderTarget("reflection")->addListener(this);

    mCamera->setAutoAspectRatio(true);
    // create our water plane mesh
    mWaterPlane = Plane(Vector3::UNIT_Y, 0);
    MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        mWaterPlane, 700, 1300, 10, 10, true, 1, 3, 5, Vector3::UNIT_Z);

    // create a water entity using our mesh, give it the shader material, and attach it to the origin
    mWater = mSceneMgr->createEntity("Water", "water");
    mWater->setMaterialName("Examples/FresnelReflectionRefraction");
    mSceneMgr->getRootSceneNode()->attachObject(mWater);

    // hide the water from the render textures
    mWater->setVisibilityFlags(WATER_MASK);
}

void windowUpdate()
{
    mWindow->update();
}

void setupProps()
{
    Entity* ent;

    // setting up props might take a while, so create a progress bar for visual feedback
    ProgressBar* pb = mTrayMgr->createProgressBar(TL_CENTER, "FresnelBuildingBar", "Creating Props...", 280, 100);
    mTrayMgr->showBackdrop("SdkTrays/Shade");

    pb->setComment("Upper Bath");
    windowUpdate();
    ent = mSceneMgr->createEntity("UpperBath", "RomanBathUpper.mesh" );
    mSceneMgr->getRootSceneNode()->attachObject(ent);        
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.4);

    pb->setComment("Columns");
    windowUpdate();
    ent = mSceneMgr->createEntity("Columns", "Columns.mesh");
    mSceneMgr->getRootSceneNode()->attachObject(ent);        
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.5);

    pb->setComment("Ogre Head");
    windowUpdate();
    ent = mSceneMgr->createEntity("Head", "ogrehead.mesh");
    ent->setMaterialName("RomanBath/OgreStone");
    mSurfaceEnts.push_back(ent);
    pb->setProgress(0.6);

    SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    headNode->setPosition(-350, 55, 130);
    headNode->yaw(Degree(90));
    headNode->attachObject(ent);

    pb->setComment("Lower Bath");
    windowUpdate();
    ent = mSceneMgr->createEntity("LowerBath", "RomanBathLower.mesh");
    mSceneMgr->getRootSceneNode()->attachObject(ent);
    mSubmergedEnts.push_back(ent);
    pb->setProgress(1);
    windowUpdate();

    mTrayMgr->destroyWidget(pb);
    mTrayMgr->hideBackdrop();
}

void setupFish()
{
    mFishNodes.resize(NUM_FISH);
    mFishAnimStates.resize(NUM_FISH);
    mFishSplines.resize(NUM_FISH);

    for (unsigned int i = 0; i < NUM_FISH; i++)
    {
        // create fish entity
        Entity* ent = mSceneMgr->createEntity("Fish" + StringConverter::toString(i + 1), "fish.mesh");
        mSubmergedEnts.push_back(ent);

        // create an appropriately scaled node and attach the entity
        mFishNodes[i] = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        mFishNodes[i]->setScale(Vector3::UNIT_SCALE * FISH_SCALE);
        mFishNodes[i]->attachObject(ent);

        // enable and save the swim animation state
        mFishAnimStates[i] = ent->getAnimationState("swim");
        mFishAnimStates[i]->setEnabled(true);

        mFishSplines[i].setAutoCalculate(false);  // save the tangent calculation for when we are all done

        // generate random waypoints for the fish to swim through
        for (unsigned int j = 0; j < NUM_FISH_WAYPOINTS; j++)
        {
            Vector3 pos(Math::SymmetricRandom() * 270, -10, Math::SymmetricRandom() * 700);

            if (j > 0)  // make sure the waypoint isn't too far from the last, or our fish will be turbo-fish
            {
                const Vector3& lastPos = mFishSplines[i].getPoint(j - 1);
                Vector3 delta = pos - lastPos;
                if (delta.length() > 750) pos = lastPos + delta.normalisedCopy() * 750;
            }

            mFishSplines[i].addPoint(pos);
        }

        // close the spline and calculate all the tangents at once
        mFishSplines[i].addPoint(mFishSplines[i].getPoint(0));
        mFishSplines[i].recalcTangents();
    }

    mFishAnimTime = 0;
}

void cleanupContent()
{
    mSurfaceEnts.clear();
    mSubmergedEnts.clear();
    mFishNodes.clear();
    mFishAnimStates.clear();
    mFishSplines.clear();

    MeshManager::getSingleton().remove("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}

const unsigned int NUM_FISH;
const unsigned int NUM_FISH_WAYPOINTS;
const unsigned int FISH_PATH_LENGTH; 
const Real FISH_SCALE;
std::vector<Entity*> mSurfaceEnts;
std::vector<Entity*> mSubmergedEnts;
Plane mWaterPlane;
Entity* mWater;
std::vector<SceneNode*> mFishNodes;
std::vector<AnimationState*> mFishAnimStates;
std::vector<SimpleSpline> mFishSplines;
Real mFishAnimTime;
};

#endif

The source code basically does this:

  • Sets different visibility flags for objects over the surface. These are rendered into the reflection texture using the compositor later.

  • Sets different visibility flags for objects under the surface. These are rendered into the refraction texture using the compositor later.

  • It activates the compositor and adds its own class as a listener to it, so it can enable/disable reflection of the camera.

  • It setups a plane where the water originates, which is a simple infinite plane with a distance (Plane(Vector3::UNIT_Y, Vector3(0, 10, 0) for a water plane that points up and is 10 meters up).

  • Creates the water mesh and applies the material to it. It also sets a visibility mask so it is never rendered into any RTT, because that would be very bad if it did.

Then the compositor takes care of the rest. It renders what it needs into the two textures and it also applies those textures to the water plane material automatically.

I followed your instructions and got a lovely fresco. However, one thing that doesn't work is that my game engine cannot be edited to upload to the Compositor, so my refraction is still a 2d texture and there is no depth nor transparency water to see I can see the bottom because when I take-off the refraction texture the whole water surface is black when I add the texture it's a 2d texture and covers the bottom of the water and nothing is visible below. So like you guys said, I need to create 2 more cameras, 1 to capture the surrounding scene, and 1 camera will shoot straight from the top to the bottom of the water and then save it to the reflective texture to show up right. And there is no way to make the texture transparent instead of reflective. So is there any way in shader can do it or do I have to add some lines of code in ogre to be able to load 2 more cameras? Thank you so much for helping me.

User avatar
sercero
Bronze Sponsor
Bronze Sponsor
Posts: 479
Joined: Sun Jan 18, 2015 4:20 pm
Location: Buenos Aires, Argentina
x 167

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by sercero »

At this point we should ask what can you change about the game engine?

Do you have the sources?

Can you recompile everything?

It seems that you can only change the shaders...

Perhaps we are misunderstanding something.

I have implemented the ThinMatrix water with OGRE, if you want I can upload the sources but it works with OpenGL3 (GLSL).

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

sercero wrote: Fri Apr 28, 2023 12:36 pm

At this point we should ask what can you change about the game engine?

Do you have the sources?

Can you recompile everything?

It seems that you can only change the shaders...

Perhaps we are misunderstanding something.

I have implemented the ThinMatrix water with OGRE, if you want I can upload the sources but it works with OpenGL3 (GLSL).

unfortunately I can't change anything with this game engine of mine because it's an old version and I don't have the source code for it, I don't even recompile it. I can just default to load the hlsl shader file to change a few things for a more realistic game. If you don't mind can I get the source code you compiled from Thin Matrix to ogre? I am very grateful for this.

User avatar
sercero
Bronze Sponsor
Bronze Sponsor
Posts: 479
Joined: Sun Jan 18, 2015 4:20 pm
Location: Buenos Aires, Argentina
x 167

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by sercero »

If you can't change the source code, then that code won't help you much but here it is anyway.

One thing you could do through shaders is use Screen Space Reflections (I believe).

It has some limitations and might be expensive in terms of GPU time.

Check this out: https://lettier.github.io/3d-game-shade ... ction.html

You do not have the required permissions to view the files attached to this post.
rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

@sercero is right.
If it is impossible for you to compile it, you can never add support for the fresnel correctly. What you want cannot only be done with shaders, it needs code to work.

Also, SSR (Screen Space Reflections) are very limited, and it would never work that good on water, so I would not even attempt to do it.
SSR is mostly used for first-person games and even there they still have a lot of visual bugs.

If I were you, I would either try to get the source code for the project or create a new project, because 99% of development is done through the source code, not through script files (like shaders).

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

sercero wrote: Fri Apr 28, 2023 5:08 pm

If you can't change the source code, then that code won't help you much but here it is anyway.

One thing you could do through shaders is use Screen Space Reflections (I believe).

It has some limitations and might be expensive in terms of GPU time.

Check this out: https://lettier.github.io/3d-game-shade ... ction.html

I think you have included shader code, but just the source I can't do anything. Anyway thank you for sharing with me

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

rpgplayerrobin wrote: Sat Apr 29, 2023 5:04 am

@sercero is right.
If it is impossible for you to compile it, you can never add support for the fresnel correctly. What you want cannot only be done with shaders, it needs code to work.

Also, SSR (Screen Space Reflections) are very limited, and it would never work that good on water, so I would not even attempt to do it.
SSR is mostly used for first-person games and even there they still have a lot of visual bugs.

If I were you, I would either try to get the source code for the project or create a new project, because 99% of development is done through the source code, not through script files (like shaders).

so I want to ask if I don't use reflection and refraction, so can I load the water surface with color and alpha channel? Will fresnel still work in this case?

rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

so I want to ask if I don't use reflection and refraction, so can I load the water surface with color and alpha channel? Will fresnel still work in this case?

I don't understand the question. What effect do you actually mean? Because the fresnel demo is using both reflection and refraction, and without either of them there is no fresnel.

phuvinh1408
Halfling
Posts: 45
Joined: Tue Aug 09, 2022 7:55 am
x 1

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by phuvinh1408 »

rpgplayerrobin wrote: Sun Apr 30, 2023 1:03 pm

so I want to ask if I don't use reflection and refraction, so can I load the water surface with color and alpha channel? Will fresnel still work in this case?

I don't understand the question. What effect do you actually mean? Because the fresnel demo is using both reflection and refraction, and without either of them there is no fresnel.

So in the previous post that you included the source code .h file for fresnel. Can I compile It into .dll file or I have to put It into OgreMain and compile It with OgreMain together?

rpgplayerrobin
Gnoll
Posts: 677
Joined: Wed Mar 18, 2009 3:03 am
x 379

Re: I need help with tansparency surface for ocean using hlsl and glsl

Post by rpgplayerrobin »

So in the previous post that you included the source code .h file for fresnel. Can I compile It into .dll file or I have to put It into OgreMain and compile It with OgreMain together?

I would not even know where to start to try to make a .dll file like that, which would dynamically load into the project and somehow run each frame.

Secondly, you never put your own user code in OgreMain, you have your project in which you put these files in.
To do that, you must first understand how to do your own project, but by doing that you still need the entire source code of the project, which you do not have.

Maybe what you mean is that you want to attempt to compile Ogre 1.7 and alter your version of those dll files to make the fresnel work, but that would probably be very difficult to do.

Even for projects where you have all the source code it might be hard to do stuff, but trying to make changes to the project without the source code is like shooting yourself in the foot and then trying to play football.

I would suggest you to not try to make changes to such a project at all, or try to get the source code, or make a new project instead.