I'm writing a game engine and have came upon a slowdown issue that is just not good enough.
I'm using Evolved's fresnel water shader (
link) and have pretty much ported his example code directly to DarkGDK, albeit I have modularized it a bit.
Here's the main code part for it; I'm using some of my own classes but I don't think that's what's slowing it down - as you can see I've timed just the would-be "sync" part from dbpro (which is wrapped as Camera::render(fastsync_boolean) in my library).
What this function does is it sets the sync mask to the given camera if it isn't already and then calls dbSync(fastSyncFlag):
#include "Ocean.h"
using namespace teril;
using namespace gdk;
Ocean::Ocean(int32_t resolution, float width, float depth, float3 pos, PShader pShader) :
elevation(pos.y), shader(pShader ? pShader : PShader(new Shader("media/water/ocean.fx", 0))), obj(Object::CreatePlane(width, depth, pos)),
wavesTex(new Image("media/water/waves.dds", Image::TEXTUREFLAG_CREATE_MIPMAPS)), fresnelTex(new Image("media/water/fresnel.bmp", Image::TEXTUREFLAG_CREATE_MIPMAPS)), mask(new Image(64, 64, 0x0)),
refractionImg(new Image(resolution, resolution)), reflectionImg(new Image(resolution, resolution)),
refractionCam(new Camera(1.0f, 5000.0f)), reflectionCam(new Camera(1.0f, 5000.0f)), timer(0, 0) {
// Setup Cameras
float aspect = (float)dbScreenWidth() / (float)dbScreenHeight();
refractionCam->setAspectRatio(aspect);
reflectionCam->setAspectRatio(aspect);
refractionCam->setBackdropEnabled(false);
reflectionCam->setBackdropEnabled(false);
refractionCam->setRenderTarget(refractionImg, resolution, resolution);
reflectionCam->setRenderTarget(reflectionImg, resolution, resolution);
refractionCam->setFOV(85.0f);
reflectionCam->setFOV(85.0f);
// Setup water plain
obj->setTexture(refractionImg, 0);
obj->setTexture(reflectionImg, 1);
obj->setTexture(wavesTex, 2);
obj->setTexture(fresnelTex, 3);
obj->setTexture(mask, 4);
obj->applyShader(shader);
obj->setTransparencyMode(Object::TRANSPARENCYMODE_DRAWFIRSTWITHALPHA);
obj->setAngleX(270.0f);
}
Ocean::~Ocean() {
// All dynamic data is held using reference counting pointers; will handle destruction by itself
}
// Note: currently returning a OceanTimer reference; this is merely for debugging issues and should be removed later on
const Ocean::OceanTimer& Ocean::tick(size_t timestamp, terrain::Terrain& terrain, Skybox& sky) {
// Hide water plane
obj->setVisible(false);
// Update refraction / reflection Cameras
shader->setTechnique("Refract");
updateRefraction(terrain, sky);
if(Camera::getMainCamera()->getPositionY() > elevation) {
shader->setTechnique("ReflectRefract");
updateReflection(sky);
} else {
// Use fog for a cheap underwater effect
// TODO: WRAP THE FOG FUNCTIONALITY IN THE CAMERA CLASS
dbFogOn();
dbFogColor(0, dbRGB(55, 65, 75));
dbFogDistance(31.0f);
terrain.setFogColour((DWORD)dbRGB(55, 65, 75));
terrain.setFogRange(21.0f, 31.0f);
terrain.enableFog(true);
}
// Show water plane
obj->setVisible(true);
return timer;
}
void Ocean::updateRefraction(terrain::Terrain& terrain, Skybox& sky) {
PCamera mainCam = Camera::getMainCamera();
refractionCam->setPosition(mainCam->getPosition());
refractionCam->setAngle(mainCam->getAngle());
if(mainCam->getPositionY() > elevation) {
refractionCam->setClipping(0, (int)(elevation + 3.0f), 0, 0, -1, 0);
dbFogOn();
dbFogColor(0, dbRGB(55, 65, 75));
dbFogDistance(12.50f + ((mainCam->getPositionY() - elevation) * 10.0f));
terrain.setFogColour((DWORD)dbRGB(55, 65, 75));
terrain.setFogRange(12.50f + ((mainCam->getPositionY() - elevation) * 10.0f), (12.50f + ((mainCam->getPositionY() - elevation) * 10.0f) * 2.0f));
terrain.enableFog(true);
} else {
refractionCam->setClipping(0, (int)(elevation - 3.0f), 0, 0, 1, 0);
dbFogOff();
terrain.enableFog(false);
}
// Reset reflection camera clipping.... (verify this should indeed be done here; that is how the example dbp code is laid out however)
reflectionCam->setClipping(0, 0, 0, 0, 0, 0, 0);
sky.frustumCull(refractionCam);
timer.refractionRender = clock();
refractionCam->render(true);
timer.refractionRender= clock() - timer.refractionRender;
dbFogOff();
terrain.enableFog(false);
}
void Ocean::updateReflection(Skybox& sky) {
PCamera mainCam = Camera::getMainCamera();
reflectionCam->setPosition(mainCam->getPositionX(), elevation - (mainCam->getPositionY() - elevation), mainCam->getPositionZ());
reflectionCam->setAngle(-mainCam->getAngleX(), mainCam->getAngleY(), mainCam->getAngleZ());
// Disable refraction camera clipping
refractionCam->setClipping(0, 0, 0, 0, 0, 0, 0);
reflectionCam->setClipping(0, (int)(elevation - 1.5f), 0, 0, 1, 0);
sky.frustumCull(reflectionCam);
timer.reflectionRender = clock();
reflectionCam->render(true);
timer.reflectionRender = clock() - timer.reflectionRender;
}
The weird thing is that the refraction camera's "sync call" takes between 40 - 80 milliseconds to complete (it differs since I'm culling objects depending on the beholder position*), while the reflection rendering lies between 1 - 14 milliseconds. The main render pass ("camera 0") is also timed and ends up at 3 - 11 milliseconds in my test scene.
Thus, it is obvious that the refraction render takes way more time than the other cameras and I just cannot figure out what would cause this. Do anybody have any ideas?
Also, although probably quite a different question, my rendering speed drops quite significantly when increasing the UV tiling on my terrain. This is also something which I don't think is usual (yes, it has mipmapping enabled) and if anybody have some information on that I would be happy to hear it as well.
Thanks for any hints,
- A frustrated Rudolpho
---------------------
* The culling information is only updated for the main camera, not for the ones used to render the shader maps here.
"Why do programmers get Halloween and Christmas mixed up?" Because Oct(31) = Dec(25)