Problems Porting Eric Bruneton's Water to XNA

Started by
3 comments, last by Budinga 11 years, 3 months ago
Hi Guyz. Well I have decided to use the great Eric Bruneton's water shaders in my procedural planet renderer, now I have managed to port the shaders, and also with a slightly modified ocean rendering class I get something on the screen, but due to the fact that he use's, a different coord system, well I think that's it I cant seem to get my brain round it. So I was wondering if anybody could spot my problem.

Original Render Function
[source]
bool DrawOceanTask::Impl::run()
{
if (Logger::DEBUG_LOGGER != NULL) {
Logger::DEBUG_LOGGER->log("OCEAN", "DrawOcean");
}
ptr<FrameBuffer> fb = SceneManager::getCurrentFrameBuffer();
ptr<Program> prog = SceneManager::getCurrentProgram();

if (o->nbWavesU == NULL) {
o->nbWavesU = prog->getUniform1f("nbWaves");
o->wavesU = prog->getUniformSampler("wavesSampler");
o->cameraToOceanU = prog->getUniformMatrix4f("cameraToOcean");
o->screenToCameraU = prog->getUniformMatrix4f("screenToCamera");
o->cameraToScreenU = prog->getUniformMatrix4f("cameraToScreen");
o->oceanToCameraU = prog->getUniformMatrix3f("oceanToCamera");
o->oceanToWorldU = prog->getUniformMatrix4f("oceanToWorld");
o->oceanCameraPosU = prog->getUniform3f("oceanCameraPos");
o->oceanSunDirU = prog->getUniform3f("oceanSunDir");
o->horizon1U = prog->getUniform3f("horizon1");
o->horizon2U = prog->getUniform3f("horizon2");
o->timeU = prog->getUniform1f("time");
o->radiusU = prog->getUniform1f("radius");
o->heightOffsetU = prog->getUniform1f("heightOffset");
o->lodsU = prog->getUniform4f("lods");

assert(o->nbWavesU != NULL);
o->generateWaves();
}

vector< ptr<TileSampler> > uniforms;
SceneNode::FieldIterator ui = n->getFields();
while (ui.hasNext()) {
ptr<TileSampler> u = ui.next().cast<TileSampler>();
if (u != NULL && u->getTerrain(0) != NULL) {
u->setTileMap();
}
}

// compute ltoo = localToOcean transform, where ocean frame = tangent space at
// camera projection on sphere o->radius in local space
mat4d ctol = n->getLocalToCamera().inverse();
vec3d cl = ctol * vec3d::ZERO; // camera in local space

if ((o->radius == 0.0 && cl.z > o->zmin) ||
(o->radius > 0.0 && cl.length() > o->radius + o->zmin) ||
(o->radius < 0.0 && vec2d(cl.y, cl.z).length() < -o->radius - o->zmin))
{
o->oldLtoo = mat4d::IDENTITY;
o->offset = vec3d::ZERO;
return true;
}

vec3d ux, uy, uz, oo;

if (o->radius == 0.0) {
// flat ocean
ux = vec3d::UNIT_X;
uy = vec3d::UNIT_Y;
uz = vec3d::UNIT_Z;
oo = vec3d(cl.x, cl.y, 0.0);
} else if (o->radius > 0.0) {
// spherical ocean
uz = cl.normalize(); // unit z vector of ocean frame, in local space
if (o->oldLtoo != mat4d::IDENTITY) {
ux = vec3d(o->oldLtoo[1][0], o->oldLtoo[1][1], o->oldLtoo[1][2]).crossProduct(uz).normalize();
} else {
ux = vec3d::UNIT_Z.crossProduct(uz).normalize();
}
uy = uz.crossProduct(ux); // unit y vector
oo = uz * o->radius; // origin of ocean frame, in local space
} else {
// cylindrical ocean
uz = vec3d(0.0, -cl.y, -cl.z).normalize();
ux = vec3d::UNIT_X;
uy = uz.crossProduct(ux);
oo = vec3d(cl.x, 0.0, 0.0) + uz * o->radius;
}

mat4d ltoo = mat4d(
ux.x, ux.y, ux.z, -ux.dotproduct(oo),
uy.x, uy.y, uy.z, -uy.dotproduct(oo),
uz.x, uz.y, uz.z, -uz.dotproduct(oo),
0.0, 0.0, 0.0, 1.0);
// compute ctoo = cameraToOcean transform
mat4d ctoo = ltoo * ctol;

if (o->oldLtoo != mat4d::IDENTITY) {
vec3d delta = ltoo * (o->oldLtoo.inverse() * vec3d::ZERO);
o->offset += delta;
}
o->oldLtoo = ltoo;

mat4d ctos = n->getOwner()->getCameraToScreen();
mat4d stoc = ctos.inverse();
vec3d oc = ctoo * vec3d::ZERO;

if (o->oceanSunDirU != NULL) {
// TODO how to get sun dir in a better way?
SceneManager::NodeIterator i = n->getOwner()->getNodes("light");
if (i.hasNext()) {
ptr<SceneNode> l = i.next();
vec3d worldSunDir = l->getLocalToParent() * vec3d::ZERO;
vec3d oceanSunDir = ltoo.mat3x3() * (n->getWorldToLocal().mat3x3() * worldSunDir);
o->oceanSunDirU->set(oceanSunDir.cast<float>());
}
}

vec4<GLint> screen = fb->getViewport();

vec4d frustum[6];
SceneManager::getFrustumPlanes(ctos, frustum);
vec3d left = frustum[0].xyz().normalize();
vec3d right = frustum[1].xyz().normalize();
float fov = (float) safe_acos(-left.dotproduct(right));
float pixelSize = atan(tan(fov / 2.0f) / (screen.w / 2.0f)); // angle under which a screen pixel is viewed from the camera

o->cameraToOceanU->setMatrix(ctoo.cast<float>());
o->screenToCameraU->setMatrix(stoc.cast<float>());
o->cameraToScreenU->setMatrix(ctos.cast<float>());
o->oceanToCameraU->setMatrix(ctoo.inverse().mat3x3().cast<float>());
o->oceanCameraPosU->set(vec3f(float(-o->offset.x), float(-o->offset.y), float(oc.z)));
if (o->oceanToWorldU != NULL) {
o->oceanToWorldU->setMatrix((n->getLocalToWorld() * ltoo.inverse()).cast<float>());
}

if (o->horizon1U != NULL) {
float h = oc.z;
vec3d A0 = (ctoo * vec4d((stoc * vec4d(0.0, 0.0, 0.0, 1.0)).xyz(), 0.0)).xyz();
vec3d dA = (ctoo * vec4d((stoc * vec4d(1.0, 0.0, 0.0, 0.0)).xyz(), 0.0)).xyz();
vec3d B = (ctoo * vec4d((stoc * vec4d(0.0, 1.0, 0.0, 0.0)).xyz(), 0.0)).xyz();
if (o->radius == 0.0) {
o->horizon1U->set(vec3f(-(h * 1e-6 + A0.z) / B.z, -dA.z / B.z, 0.0));
o->horizon2U->set(vec3f::ZERO);
} else {
double h1 = h * (h + 2.0 * o->radius);
double h2 = (h + o->radius) * (h + o->radius);
double alpha = B.dotproduct(B) * h1 - B.z * B.z * h2;
double beta0 = (A0.dotproduct(B) * h1 - B.z * A0.z * h2) / alpha;
double beta1 = (dA.dotproduct(B) * h1 - B.z * dA.z * h2) / alpha;
double gamma0 = (A0.dotproduct(A0) * h1 - A0.z * A0.z * h2) / alpha;
double gamma1 = (A0.dotproduct(dA) * h1 - A0.z * dA.z * h2) / alpha;
double gamma2 = (dA.dotproduct(dA) * h1 - dA.z * dA.z * h2) / alpha;
o->horizon1U->set(vec3f(-beta0, -beta1, 0.0));
o->horizon2U->set(vec3f(beta0 * beta0 - gamma0, 2.0 * (beta0 * beta1 - gamma1), beta1 * beta1 - gamma2));
}
}

o->timeU->set(n->getOwner()->getTime() * 1e-6);
if (o->radiusU != NULL) {
o->radiusU->set(o->radius < 0.0 ? -o->radius : o->radius);
}
o->heightOffsetU->set(-o->meanHeight);
o->lodsU->set(vec4f(o->resolution,
pixelSize * o->resolution,
log(o->lambdaMin) / log(2.0f),
(o->nbWavesU->get() - 1.0f) / (log(o->lambdaMax) / log(2.0f) - log(o->lambdaMin) / log(2.0f))));

if (o->screenGrid == NULL || o->screenWidth != screen.z || o->screenHeight != screen.w) {
o->screenWidth = screen.z;
o->screenHeight = screen.w;
o->screenGrid = new Mesh<vec2f, unsigned int>(TRIANGLES, GPU_STATIC);
o->screenGrid->addAttributeType(0, 2, A32F, false);

float f = 1.25f;
int NX = int(f * screen.z / o->resolution);
int NY = int(f * screen.w / o->resolution);
for (int i = 0; i < NY; ++i) {
for (int j = 0; j < NX; ++j) {
o->screenGrid->addVertex(vec2f(2.0*f*j/(NX-1.0f)-f, 2.0*f*i/(NY-1.0f)-f));
}
}
for (int i = 0; i < NY-1; ++i) {
for (int j = 0; j < NX-1; ++j) {
o->screenGrid->addIndice(i*NX+j);
o->screenGrid->addIndice(i*NX+j+1);
o->screenGrid->addIndice((i+1)*NX+j);
o->screenGrid->addIndice((i+1)*NX+j);
o->screenGrid->addIndice(i*NX+j+1);
o->screenGrid->addIndice((i+1)*NX+j+1);
}
}
}

fb->draw(prog, *(o->screenGrid));

return true;
}
[/source]

Now my update function (basically tries to do the same thing.

[source]
public void Update(GameTime gametime)
{



// compute ltoo = localToOcean transform, where ocean frame = tangent space at
// camera projection on sphere o->radius in local space
//Matrix.CreateRotationY (MathHelper.ToRadians(90)) * Matrix.CreateRotationZ (MathHelper.ToRadians(90))*
Matrix ctol = Matrix.Identity * Matrix.Invert(Parent.cam.View);
Vector3 cl = Vector3.Transform(Vector3.Zero, ctol);//camera in local space

if ((radius == 0.0 && cl.Z > zmin) ||
(radius > 0.0 && cl.Length() > radius + zmin) ||
(radius < 0.0 && new Vector2(cl.Y, cl.Z).Length() < -radius - zmin))
{
oldLtoo = Matrix.Identity;
offset = Vector3.Zero;
return;
}

Vector3 ux, uy, uz, oo;

if (radius == 0.0)
{
// flat ocean
ux = Vector3.UnitX;
uy = Vector3.UnitY;
uz = Vector3.UnitZ;
oo = new Vector3(cl.X, cl.Y, 0.0f);
}
else if (radius > 0.0)
{
// spherical ocean
uz = Vector3.Normalize(cl);// unit z vector of ocean frame, in local space

if (oldLtoo != Matrix.Identity)
{
//Matrix m = Matrix.Transpose(oldLtoo);
//ux = vec3d(o->oldLtoo[1][0], o->oldLtoo[1][1], o->oldLtoo[1][2]).crossProduct(uz).normalize();
ux = Vector3.Normalize(Vector3.Cross(new Vector3(oldLtoo.M21, oldLtoo.M22, oldLtoo.M23), uz));
}
else
{
ux = Vector3.Normalize(Vector3.Cross(Vector3.UnitZ, uz));
}


uy = Vector3.Cross(uz, ux);// unit y vector // negate this ??
//oo = uz * radius; // origin of ocean frame, in local space
oo = ux * radius;

}
else
{
// cylindrical ocean
uz = Vector3.Normalize(new Vector3(0, -cl.Y, -cl.Z));
ux = Vector3.UnitX;
uy = Vector3.Cross(uz, ux);
oo = new Vector3(cl.X, 0, 0) + uz * radius;
}


Matrix ltoo = new Matrix(
ux.X, uy.X, uz.X, 0.0f,
ux.Y, uy.Y, uz.Y, 0.0f,
ux.Z, uy.Z, uz.Z, 0.0f,
-Vector3.Dot(ux, oo), -Vector3.Dot(uy, oo), -Vector3.Dot(uz, oo), 1.0f);



ltoo = Matrix.Transpose(ltoo);





// compute ctoo = cameraToOcean transform
Matrix ctoo = ltoo * Matrix.Transpose (ctol);





if (oldLtoo != Matrix.Identity)
{
Vector3 delta = Vector3.Transform(Vector3.Zero, Matrix.Transpose ( ltoo * Matrix.Invert(oldLtoo)));
offset += delta;
}

oldLtoo = ltoo;


Matrix ctos = Matrix.Transpose (Parent.cam.Projection); //world*view*projection //n->getOwner()->getCameraToScreen();
Matrix stoc = Matrix.Invert (ctos);
Vector3 oc = Vector3.Transform(Vector3.Zero, ctoo);



Vector3 worldsunDir = Parent.vLightDirection;
Vector3 oceanSunDir = Vector3.Transform(worldsunDir, ltoo * Parent.cam.View);

BoundingFrustum frustum = new BoundingFrustum(ctos);
Vector3 left = frustum.Left.Normal;
Vector3 right = frustum.Right.Normal;
float fov = (float)System.Math.Acos(Vector3.Dot(-left, right));

float pixelSize = (float)System.Math.Atan(System.Math.Tan(fov / 2.0f) / (device.Viewport.Height / 2.0f));// angle under which a screen pixel is viewed from the camera


float h = oc.Z;

Vector4 tmp1,tmp2;

tmp1 = Vector4.Transform ( new Vector4 (0,0,0,1),stoc);
tmp2 = Vector4.Transform (new Vector4 (tmp1.X,tmp1.Y,tmp1.Z,0),ctoo);
Vector3 A0 = new Vector3 (tmp2.X,tmp2.Y,tmp2.Z);

tmp1 = Vector4.Transform ( new Vector4 (1,0,0,0),stoc);
tmp2 = Vector4.Transform (new Vector4 (tmp1.X,tmp1.Y,tmp1.Z,0),ctoo);
Vector3 dA = new Vector3 (tmp2.X,tmp2.Y,tmp2.Z);

tmp1 = Vector4.Transform ( new Vector4 (0,1,0,0),stoc);
tmp2 = Vector4.Transform (new Vector4 (tmp1.X,tmp1.Y,tmp1.Z,0),ctoo);
Vector3 B = new Vector3 (tmp2.X,tmp2.Y,tmp2.Z);

if (radius == 0.0)
{
horizon1U = new Vector3(-(h * 1e-6f + A0.Z) / B.Z, -dA.Z / B.Z, 0);
horizon2U = Vector3.Zero;
}
else
{
double h1 = h * (h + 2.0 * radius);
double h2 = (h + radius) * (h + radius);
double alpha = Vector3.Dot (B,B) * h1 - B.Z * B.Z * h2;
double beta0 = (Vector3.Dot (A0,B) * h1 - B.Z * A0.Z * h2) / alpha;
double beta1 = (Vector3.Dot (dA,B) * h1 - B.Z * dA.Z * h2) / alpha;
double gamma0 = (Vector3.Dot (A0,A0) * h1 - A0.Z * A0.Z * h2) / alpha;
double gamma1 = (Vector3.Dot(A0,dA) * h1 - A0.Z * dA.Z * h2) / alpha;
double gamma2 = (Vector3.Dot(dA,dA) * h1 - dA.Z * dA.Z * h2) / alpha;
horizon1U = new Vector3((float)-beta0, (float)-beta1, 0);
horizon2U = new Vector3((float)beta0 * (float)beta0 - (float)gamma0, 2.0f * ((float)beta0 * (float)beta1 - (float)gamma1), (float)beta1 * (float)beta1 - (float)gamma2);

}



timeU = (float)gametime.TotalGameTime.TotalMilliseconds *0.0001f;

heightOffsetU = -meanHeight;

lodsU = new Vector4(resolution,
pixelSize * resolution,
(float)System.Math.Log(lambdaMin) / (float)System.Math.Log(2.0f),
(nbWaves - 1.0f) / ((float)System.Math.Log(lambdaMax) / (float)System.Math.Log(2.0f) - (float)System.Math.Log(lambdaMin) / (float)System.Math.Log(2.0f)));



cameraToOceanU = Matrix.Transpose (ctoo);
screenToCameraU = stoc;
cameraToScreenU = ctos;


oceanCameraPosU = new Vector3(-offset.X, -offset.Y, -offset.Z);

oceanToWorldU = Matrix.Transpose ( Parent.cam.Projection * Parent.cam.View) * Matrix.Invert (ltoo);


oceanToCameraU = Matrix.Transpose(Matrix.Invert(ctoo));

//Global Variables
watereffect.Parameters["cameraToOcean"].SetValue(cameraToOceanU);
watereffect.Parameters["screenToCamera"].SetValue(screenToCameraU); // World Projection Matrix Inverse
watereffect.Parameters["cameraToScreen"].SetValue (cameraToScreenU); // World Projection Matrix
watereffect.Parameters["oceanToCamera"].SetValue(oceanToCameraU);
watereffect.Parameters["oceanToWorld"].SetValue(oceanToWorldU);
watereffect.Parameters["oceanCameraPos"].SetValue(oceanCameraPosU);
watereffect.Parameters["oceanSunDir"].SetValue(oceanSunDir);
watereffect.Parameters["horizon1"].SetValue(horizon1U);
watereffect.Parameters["horizon2"].SetValue(horizon2U);
watereffect.Parameters["worldCameraPos"].SetValue(Parent.cam.Position);
watereffect.Parameters["worldSunDir"].SetValue(worldsunDir);
watereffect.Parameters["seaColor"].SetValue(seaColor);
watereffect.Parameters["seaRoughness"].SetValue(sigmaXsq);
watereffect.Parameters["radius"].SetValue(radius);
watereffect.Parameters["nbWaves"].SetValue(nbWaves); // number of waves
watereffect.Parameters["heightOffset"].SetValue(heightOffsetU); ; // so that surface height is centered around z = 0
watereffect.Parameters["time"].SetValue (timeU); // current time
// grid cell size in pixels, angle under which a grid cell is seen,
// and parameters of the geometric series used for wavelengths
watereffect.Parameters["lods"].SetValue(lodsU);



if (screengrid == null || screenWidth != device.Viewport.Width || screenHeight != device.Viewport.Height )
{
screenWidth = device.Viewport.Width;
screenHeight = device.Viewport.Height;

screengrid = new ScreenGrid ();


float f = 1.25f;
int NX = (int)(f * screenWidth / resolution);
int NY = (int)(f * screenHeight / resolution);

for (int i = 0; i < NY; ++i)
{
for (int j = 0; j < NX; ++j)
{
screengrid.AddVertex (new Vector2(2.0f*f*j/(NX-1.0f)-f, 2.0f*f*i/(NY-1.0f)-f));
}
}


for (int i = 0; i < NY-1; ++i) {
for (int j = 0; j < NX-1; ++j) {
screengrid.AddIndices(i * NX + j);
screengrid.AddIndices(i * NX + j + 1);
screengrid.AddIndices((i + 1) * NX + j);
screengrid.AddIndices((i + 1) * NX + j);
screengrid.AddIndices(i * NX + j + 1);
screengrid.AddIndices((i + 1) * NX + j + 1);
}
}


screengrid.UpdateBuffers(device);
}



}

[/source]
and now for the shader code

original code

[source]
/*
* Proland: a procedural landscape rendering library.
* Copyright (c) 2008-2011 INRIA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Proland is distributed under a dual-license scheme.
* You can obtain a specific license from Inria: proland-licensing@inria.fr.
*/
/*
* Authors: Eric Bruneton, Antoine Begault, Guillaume Piolat.
*/
#include "globalsShader.glhl"
#include "textureTile.glsl"
const float Z0 = 1.0;
uniform float radius;
uniform mat4 cameraToOcean;
uniform mat4 screenToCamera;
uniform mat4 cameraToScreen;
uniform mat3 oceanToCamera;
uniform mat4 oceanToWorld;
uniform vec3 oceanCameraPos;
uniform vec3 oceanSunDir;
uniform vec3 horizon1;
uniform vec3 horizon2;
uniform float nbWaves; // number of waves
uniform sampler1D wavesSampler; // waves parameters (h, omega, kx, ky) in wind space
uniform float heightOffset; // so that surface height is centered around z = 0
uniform float time; // current time
// grid cell size in pixels, angle under which a grid cell is seen,
// and parameters of the geometric series used for wavelengths
uniform vec4 lods;
#define NYQUIST_MIN 0.5 // wavelengths below NYQUIST_MIN * sampling period are fully attenuated
#define NYQUIST_MAX 1.25 // wavelengths above NYQUIST_MAX * sampling period are not attenuated at all
const float g = 9.81;
const float PI = 3.141592657;
#ifdef _VERTEX_
layout(location=0) in vec3 vertex;
out float oceanLod;
out vec2 oceanUv; // coordinates in wind space used to compute P(u)
out vec3 oceanP; // wave point P(u) in ocean space
out vec3 oceanDPdu; // dPdu in wind space, used to compute N
out vec3 oceanDPdv; // dPdv in wind space, used to compute N
out float oceanRoughness; // variance of unresolved waves in wind space
vec2 oceanPos(vec3 vertex, out float t, out vec3 cameraDir, out vec3 oceanDir) {
float horizon = horizon1.x + horizon1.y * vertex.x - sqrt(horizon2.x + (horizon2.y + horizon2.z * vertex.x) * vertex.x);
cameraDir = normalize((screenToCamera * vec4(vertex.x, min(vertex.y, horizon), 0.0, 1.0)).xyz);
oceanDir = (cameraToOcean * vec4(cameraDir, 0.0)).xyz;
float cz = oceanCameraPos.z;
float dz = oceanDir.z;
if (radius == 0.0) {
t = (heightOffset + Z0 - cz) / dz;
} else {
float b = dz * (cz + radius);
float c = cz * (cz + 2.0 * radius);
float tSphere = - b - sqrt(max(b * b - c, 0.0));
float tApprox = - cz / dz * (1.0 + cz / (2.0 * radius) * (1.0 - dz * dz));
t = abs((tApprox - tSphere) * dz) < 1.0 ? tApprox : tSphere;
}
return oceanCameraPos.xy + t * oceanDir.xy;
}
vec2 oceanPos(vec3 vertex) {
float t;
vec3 cameraDir;
vec3 oceanDir;
return oceanPos(vertex, t, cameraDir, oceanDir);
}
void main() {
float t;
vec3 cameraDir;
vec3 oceanDir;
vec2 uv = oceanPos(vertex, t, cameraDir, oceanDir);
float lod = - t / oceanDir.z * lods.y; // size in meters of one grid cell, projected on the sea surface
vec2 duv = oceanPos(vertex + vec3(0.0, 0.01, 0.0)) - uv;
vec3 dP = vec3(0.0, 0.0, heightOffset + (radius > 0.0 ? 0.0 : Z0));
vec3 dPdu = vec3(1.0, 0.0, 0.0);
vec3 dPdv = vec3(0.0, 1.0, 0.0);
float roughness = getSeaRoughness();
if (duv.x != 0.0 || duv.y != 0.0) {
float iMin = max(floor((log2(NYQUIST_MIN * lod) - lods.z) * lods.w), 0.0);
for (float i = iMin; i < nbWaves; ++i) {
vec4 wt = textureLod(wavesSampler, (i + 0.5) / nbWaves, 0.0);
float phase = wt.y * time - dot(wt.zw, uv);
float s = sin(phase);
float c = cos(phase);
float overk = g / (wt.y * wt.y);
float wm = smoothstep(NYQUIST_MIN, NYQUIST_MAX, (2.0 * PI) * overk / lod);
vec3 factor = wm * wt.x * vec3(wt.zw * overk, 1.0);
dP += factor * vec3(s, s, c);
vec3 dPd = factor * vec3(c, c, -s);
dPdu -= dPd * wt.z;
dPdv -= dPd * wt.w;
wt.zw *= overk;
float kh = wt.x / overk;
roughness -= wt.z * wt.z * (1.0 - sqrt(1.0 - kh * kh));
}
}
vec3 p = t * oceanDir + dP + vec3(0.0, 0.0, oceanCameraPos.z);
if (radius > 0.0) {
dPdu += vec3(0.0, 0.0, -p.x / (radius + p.z));
dPdv += vec3(0.0, 0.0, -p.y / (radius + p.z));
}
gl_Position = cameraToScreen * vec4(t * cameraDir + oceanToCamera * dP, 1.0);
oceanLod = lod;
oceanUv = uv;
oceanP = p;
oceanDPdu = dPdu;
oceanDPdv = dPdv;
oceanRoughness = roughness;
}
#endif
#ifdef _FRAGMENT_
#include "atmosphereShader.glhl"
#include "oceanBrdf.glhl"
#include "clouds.glhl"
in float oceanLod;
in vec2 oceanUv;
in vec3 oceanP;
in vec3 oceanDPdu;
in vec3 oceanDPdv;
in float oceanRoughness;
layout(location=0) out vec4 data;
void main() {
vec3 WSD = getWorldSunDir();
vec3 WCP = getWorldCameraPos();
float lod = oceanLod;
vec2 uv = oceanUv;
vec3 dPdu = oceanDPdu;
vec3 dPdv = oceanDPdv;
float roughness = oceanRoughness;
float iMAX = min(ceil((log2(NYQUIST_MAX * lod) - lods.z) * lods.w), nbWaves - 1.0);
float iMax = floor((log2(NYQUIST_MIN * lod) - lods.z) * lods.w);
float iMin = max(0.0, floor((log2(NYQUIST_MIN * lod / lods.x) - lods.z) * lods.w));
for (float i = iMin; i <= iMAX; i += 1.0) {
vec4 wt = textureLod(wavesSampler, (i + 0.5) / nbWaves, 0.0);
float phase = wt.y * time - dot(wt.zw, uv);
float s = sin(phase);
float c = cos(phase);
float overk = g / (wt.y * wt.y);
float wm = smoothstep(NYQUIST_MIN, NYQUIST_MAX, (2.0 * PI) * overk / lod);
float wn = smoothstep(NYQUIST_MIN, NYQUIST_MAX, (2.0 * PI) * overk / lod * lods.x);
vec3 factor = (1.0 - wm) * wn * wt.x * vec3(wt.zw * overk, 1.0);
vec3 dPd = factor * vec3(c, c, -s);
dPdu -= dPd * wt.z;
dPdv -= dPd * wt.w;
wt.zw *= overk;
float kh = i < iMax ? wt.x / overk : 0.0;
float wkh = (1.0 - wn) * kh;
roughness -= wt.z * wt.z * (sqrt(1.0 - wkh * wkh) - sqrt(1.0 - kh * kh));
}
roughness = max(roughness, 1e-5);
vec3 earthCamera = vec3(0.0, 0.0, oceanCameraPos.z + radius);
vec3 earthP = radius > 0.0 ? normalize(oceanP + vec3(0.0, 0.0, radius)) * (radius + 10.0) : oceanP;
vec3 oceanCamera = vec3(0.0, 0.0, oceanCameraPos.z);
vec3 V = normalize(oceanCamera - oceanP);
vec3 N = normalize(cross(dPdu, dPdv));
if (dot(V, N) < 0.0) {
N = reflect(N, V); // reflects backfacing normals
}
vec3 sunL;
vec3 skyE;
vec3 extinction;
sunRadianceAndSkyIrradiance(earthP, N, oceanSunDir, sunL, skyE);
vec3 worldP = (oceanToWorld * vec4(oceanP, 1.0)).xyz;
sunL *= cloudsShadow(worldP, WSD, 0.0, dot(normalize(worldP), WSD), radius);
vec3 surfaceColor = oceanRadiance(V, N, oceanSunDir, roughness, sunL, skyE);
// aerial perspective
vec3 inscatter = inScattering(earthCamera, earthP, oceanSunDir, extinction, 0.0);
vec3 finalColor = surfaceColor * extinction + inscatter;
data.rgb = hdr(finalColor);
data.a = 1.0;
}
#endif
[/source]

and now for my code

[source]
//Global Variables
float4x4 cameraToOcean;
float4x4 screenToCamera; // World Projection Matrix Inverse
float4x4 cameraToScreen; // World Projection Matrix
float3x3 oceanToCamera;
float4x4 oceanToWorld;
float3 oceanCameraPos;
float3 oceanSunDir;
float3 horizon1;
float3 horizon2;
float3 worldCameraPos;
float3 worldSunDir;
float3 seaColor;
float hdrExposure = 0.3f;
float seaRoughness;
float radius;
float nbWaves; // number of waves
float heightOffset; // so that surface height is centered around z = 0
float time; // current time
// grid cell size in pixels, angle under which a grid cell is seen,
// and parameters of the geometric series used for wavelengths
float4 lods;
?
//Constants
#define PI 3.141592657
const float Z0 = 1.0;
const float g = 9.81;
#define NYQUIST_MIN 0.5 // wavelengths below NYQUIST_MIN * sampling period are fully attenuated
#define NYQUIST_MAX 1.25 // wavelengths above NYQUIST_MAX * sampling period are not attenuated at all
//Textures
texture wavesTexture; // waves parameters (h, omega, kx, ky) in wind space texture2d
sampler wavesSampler = sampler_state
{
texture = <wavesTexture>;
magfilter = POINT;
minfilter = POINT;
mipfilter = POINT;
AddressU = CLAMP;
AddressV = CLAMP;
};
?
struct VertexShaderInput
{
float4 Position : POSITION0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float oceanLod : TEXCOORD0;
float2 oceanUv : TEXCOORD1; // coordinates in wind space used to compute P(u)
float3 oceanP: TEXCOORD2; // wave point P(u) in ocean space
float3 oceanDPdu: TEXCOORD3; // dPdu in wind space, used to compute N
float3 oceanDPdv: TEXCOORD4; // dPdv in wind space, used to compute N
float oceanRoughness: TEXCOORD5; // variance of unresolved waves in wind space
};
//--------------------------------------------------------------
//Utilitie Function Section
//--------------------------------------------------------------
float3 hdr(float3 L) {
#ifndef NOHDR
L = L * hdrExposure;
L.r = L.r < 1.413 ? pow(L.r * 0.38317, 1.0 / 2.2) : 1.0 - exp(-L.r);
L.g = L.g < 1.413 ? pow(L.g * 0.38317, 1.0 / 2.2) : 1.0 - exp(-L.g);
L.b = L.b < 1.413 ? pow(L.b * 0.38317, 1.0 / 2.2) : 1.0 - exp(-L.b);
#endif
return L;
}
//---------------------------------------------
// Fresnel Functions
//---------------------------------------------
float meanFresnel(float cosThetaV, float sigmaV) {
return pow(1.0 - cosThetaV, 5.0 * exp(-2.69 * sigmaV)) / (1.0 + 22.7 * pow(sigmaV, 1.5));
}
float meanFresnel(float3 V, float3 N, float sigmaSq) {
return meanFresnel(dot(V, N), sqrt(sigmaSq));
}
//-----------------------------------------------
//Radiance Functions
//-----------------------------------------------
float reflectedSunRadiance(float3 V, float3 N, float3 L, float sigmaSq) {
float3 H = normalize(L + V);
float hn = dot(H, N);
float p = exp(-2.0 * ((1.0 - hn * hn) / sigmaSq) / (1.0 + hn)) / (4.0 * PI * sigmaSq);
float c = 1.0 - dot(V, H);
float c2 = c * c;
float fresnel = 0.02 + 0.98 * c2 * c2 * c;
float zL = dot(L, N);
float zV = dot(V, N);
zL = max(zL, 0.01);
zV = max(zV, 0.01);
// brdf times cos(thetaL)
return zL <= 0.0 ? 0.0 : max(fresnel * p * sqrt(abs(zL / zV)), 0.0);
}
float3 oceanRadiance(float3 V, float3 N, float3 L, float seaRoughness, float3 sunL, float3 skyE) {
float F = meanFresnel(V, N, seaRoughness);
float3 Lsun = reflectedSunRadiance(V, N, L, seaRoughness) * sunL;
float3 Lsky = skyE * F / PI;
float3 Lsea = (1.0 - F) * seaColor * skyE / PI;
return Lsun + Lsky + Lsea;
}
//--------------------------------------------------------------
//Ocean Position Functions
//--------------------------------------------------------------
float2 oceanPos(float3 vertex, out float t, out float3 cameraDir, out float3 oceanDir)
{
float horizon = horizon1.x + horizon1.y * vertex.x - sqrt(horizon2.x + (horizon2.y + horizon2.z * vertex.x) * vertex.x);
cameraDir = normalize((mul(screenToCamera , float4(vertex.x, min(vertex.y, horizon), 0.0, 1.0))).xyz);
oceanDir = (mul(cameraToOcean , float4(cameraDir, 0.0))).xyz;
float cz = oceanCameraPos.z;
float dz = oceanDir.z;
if (radius == 0.0) {
t = (heightOffset + Z0 - cz) / dz;
} else {
float b = dz * (cz + radius);
float c = cz * (cz + 2.0 * radius);
float tSphere = - b - sqrt(max(b * b - c, 0.0));
float tApprox = - cz / dz * (1.0 + cz / (2.0 * radius) * (1.0 - dz * dz));
t = abs((tApprox - tSphere) * dz) < 1.0 ? tApprox : tSphere;
}
return oceanCameraPos.xy + t * oceanDir.xy;
}
?
float2 oceanPos(float3 vertex) {
float t;
float3 cameraDir;
float3 oceanDir;
return oceanPos(vertex, t, cameraDir, oceanDir);
}
?
?
//--------------------------------------------------------------
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output = (VertexShaderOutput)0;
float t;
float3 cameraDir;
float3 oceanDir;
float2 uv = oceanPos(input.Position.xyz, t, cameraDir, oceanDir);
float lod = -t / oceanDir.y * lods.y; //-t / oceanDir.z * lods.y; // size in meters of one grid cell, projected on the sea surface
float2 duv = oceanPos(input.Position.xyz + float3(0.0, 0.01, 0.0)) - uv; // maybe y not z

//float4 tempPos = float4(worldPos.x, 0, worldPos.y, 1.0f);
//worldPos /= 2000.0f;
//worldPos += 0.5f / 512.0f;
float3 dP = float3(0.0, 0.0, heightOffset + (radius > 0.0 ? 0.0 : Z0)); // maybe y not z
float3 dPdu = float3(1.0, 0.0, 0.0); // maybe y not z
float3 dPdv = float3(0.0, 1.0, 0.0); // maybe y not z
?
float roughness = seaRoughness;
if (duv.x != 0.0 || duv.y != 0.0) {
float iMin = max(floor((log2(NYQUIST_MIN * lod) - lods.z) * lods.w), 0.0);
for (float i = iMin; i < nbWaves; ++i)
{
float4 wt = tex1Dlod(wavesSampler, (i + 0.5) / nbWaves);
float phase = wt.y * time - dot(wt.zw, uv);
float s = sin(phase);
float c = cos(phase);
float overk = g / (wt.y * wt.y);
float wm = smoothstep(NYQUIST_MIN, NYQUIST_MAX, (2.0 * PI) * overk / lod);
float3 factor = wm * wt.x * float3(wt.zw * overk, 1.0);
dP += factor * float3(s, s, c);
float3 dPd = factor * float3(c, c, -s);
dPdu -= dPd * wt.z;
dPdv -= dPd * wt.w;
wt.zw *= overk;
float kh = wt.x / overk;
roughness -= wt.z * wt.z * (1.0 - sqrt(1.0 - kh * kh));
}
}
?
?
?
float3 p = t * oceanDir + dP + float3(0.0, 0.0, oceanCameraPos.z); // maybe y not z
if (radius > 0.0) {
dPdu += float3(0.0, 0.0, -p.x / (radius + p.z));
dPdv += float3(0.0, 0.0, -p.y / (radius + p.z));
}

output.Position = mul(cameraToScreen, float4(t * cameraDir + mul(dP,oceanToCamera), 1.0));
//output.Position = mul(cameraToScreen,float4(input.Position.xyz,1));
//logrithmic zbuffer.
const float Cc = 1.0;
const float Far = 1000000000000.0;
output.Position.z = log(Cc*output.Position.z + 1) / log(Cc*Far + 1) * output.Position.w;
output.oceanLod = lod;
output.oceanUv = uv;
output.oceanP = p;
output.oceanDPdu = dPdu;
output.oceanDPdv = dPdv;
output.oceanRoughness = roughness;
return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
float3 WSD = worldSunDir;
float3 WCP = worldCameraPos;
float lod = input.oceanLod;
float2 uv = input.oceanUv;
float3 dPdu = input.oceanDPdu;
float3 dPdv = input.oceanDPdv;
float roughness = input.oceanRoughness;
float iMAX = min(ceil((log2(NYQUIST_MAX * lod) - lods.z) * lods.w), nbWaves - 1.0);
float iMax = floor((log2(NYQUIST_MIN * lod) - lods.z) * lods.w);
float iMin = max(0.0, floor((log2(NYQUIST_MIN * lod / lods.x) - lods.z) * lods.w));
for (float i = iMin; i <= iMAX; i += 1.0) {
float4 wt = tex1Dlod(wavesSampler, (i + 0.5) / nbWaves);
float phase = wt.y * time - dot(wt.zw, uv);
float s = sin(phase);
float c = cos(phase);
float overk = g / (wt.y * wt.y);
float wm = smoothstep(NYQUIST_MIN, NYQUIST_MAX, (2.0 * PI) * overk / lod);
float wn = smoothstep(NYQUIST_MIN, NYQUIST_MAX, (2.0 * PI) * overk / lod * lods.x);
float3 factor = (1.0 - wm) * wn * wt.x * float3(wt.zw * overk, 1.0);
float3 dPd = factor * float3(c, c, -s);
dPdu -= dPd * wt.z;
dPdv -= dPd * wt.w;
wt.zw *= overk;
float kh = i < iMax ? wt.x / overk : 0.0;
float wkh = (1.0 - wn) * kh;
roughness -= wt.z * wt.z * (sqrt(1.0 - wkh * wkh) - sqrt(1.0 - kh * kh));
}
roughness = max(roughness, 1e-5);
float3 earthCamera = float3(0.0, 0.0, oceanCameraPos.z + radius);
float3 earthP = radius > 0.0 ? normalize(input.oceanP + float3(0.0, 0.0, radius)) * (radius + 10.0) : input.oceanP;
float3 oceanCamera = float3(0.0, 0.0, oceanCameraPos.z);
float3 V = normalize(oceanCamera - input.oceanP);
float3 N = normalize(cross(dPdu, dPdv));
if (dot(V, N) < 0.0) {
N = reflect(N, V); // reflects backfacing normals
}
float3 sunL = (float3)1;
float3 skyE = (float3)1;
float3 extinction = (float3)1;
//sunRadianceAndSkyIrradiance(earthP, N, oceanSunDir, sunL, skyE);
float3 worldP = (mul(oceanToWorld , float4(input.oceanP, 1.0))).xyz;
//sunL *= cloudsShadow(worldP, WSD, 0.0, dot(normalize(worldP), WSD), radius);
float3 surfaceColor = oceanRadiance(V, N, oceanSunDir, roughness, sunL, skyE);
// aerial perspective
float3 inscatter = (float3)1; //= inScattering(earthCamera, earthP, oceanSunDir, extinction, 0.0);
float3 finalColor = surfaceColor * extinction + inscatter;
float4 data;
data.rgb = hdr(finalColor);
data.a = 1.0;
return data;
//return float4(1, 0, 0, 1);
}
technique Technique1
{
pass Pass1
{
// TODO: set renderstates here.
VertexShader = compile vs_3_0 VertexShaderFunction();
PixelShader = compile ps_3_0 PixelShaderFunction();
}
}
[/source]

and now a picture of the result im getting.


Bare in mind this is suppose to be a sphere, it looks like its half working must be something to do with the y/z axis. I know its not a good thing to just post code and ask for help but im going nuts trying to figure this out any help will be much appreciated.

Budinga

screenshot1120139215.jpg

and this is the flat version it seems to be working but the projection and the surface its projecting is been rendered very strange.

screenshot1120131046.jpg
Advertisement

I successfully ported his examples to directx and ran into the same issues. Unfortunately, there were several places where the y and z components needed to be flipped. Both in the vertex shader and the pixel shader. First, get your vertex shader to draw properly.

Swap your final output y and z components in the vertex shader( but before you transform the vertex so the transform is correct).

This will mess up your pixel shader. So try swaping the z and y components back at the beginning of the pixel shader.

Then, make sure to swap the z and y components of anything externally set and used in all of the shaders, i.e. cameraposition, sunnormal

Wisdom is knowing when to shut up, so try it.
--Game Development http://nolimitsdesigns.com: Reliable UDP library, Threading library, Math Library, UI Library. Take a look, its all free.

Hi,

Thanx for your answer I gathered it was something like that, the main thing I need to ask is did you transpose your Matrix before you passed them to the shader or did you just pass them the same way as the example and just change the y and z components. I did actually try swapping the whole everything first and I got nothing so I had to start again.

Ok I am getting some ware now on the flat plane ocean, just gotta work on the spherical one. Just wanted to ask some advice on one more thing. When I load the precomputed scattering textures any advice on how to do this in xna, ive used the io.file to load the raw data I have tried to load it single by single. Af far as I can see he uses a half float to store each pixel, how the heck do I load this in to xna.

P.S still don't have the vertex shader working100% thanx smasherprog at least I am now getting someware. :)

Ok a little update I managed to figure out a few things now the flat version I found quite simple to get working , but I am aiming for the spherical version and I am stuck on the code for the matrix generation if anybody can give me a little insite in to what im doing wrong. At the moment I am using this code, but I am sure it dosent work properly as I only get a broken up sphere , so is there something im doing wrong do I need to construct the matrix in a different way.

Vector3 ux, uy, uz, oo; if (radius == 0) { // flat ocean ux = Vector3.UnitX; uy = Vector3.UnitY; uz = Vector3.UnitZ; oo = new Vector3(camtolocal.X, camtolocal.Y,0); } else { // spherical ocean uz = Vector3.Normalize(camtolocal);// unit z vector of ocean frame, in local space ? if (oldcamtolocal != Matrix.Identity) { ux = Vector3.Normalize(Vector3.Cross(new Vector3(oldcamtolocal.M21, oldcamtolocal.M22, oldcamtolocal.M23), uz)); ? ? } else { ux = Vector3.Normalize(Vector3.Cross(Vector3.UnitZ, uz)); } uy = Vector3.Cross(uz, ux);// unit y vector // negate this ?? oo = uz * radius; // origin of ocean frame, in local space } Matrix localtoocean = new Matrix( ux.X, ux.Y, ux.Z, -Vector3.Dot(ux, oo), uy.X, uy.Y, uy.Z, -Vector3.Dot(uy, oo), uz.X, uz.Y, uz.Z, -Vector3.Dot(uz, oo), 0.0f, 0.0f, 0.0f, 1.0f);

This topic is closed to new replies.

Advertisement