1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-14 17:14:29 +02:00

#23 volumetric caustics effect

This commit is contained in:
XProger
2018-09-23 04:48:54 +03:00
parent 3c5eb00dbe
commit fdeeb731ef
9 changed files with 430 additions and 34 deletions

View File

@@ -91,8 +91,9 @@ struct ShaderCache {
void prepareWater(int fx) {
compile(Core::passWater, Shader::WATER_MASK, fx, RS_COLOR_WRITE_A | RS_DEPTH_TEST);
compile(Core::passWater, Shader::WATER_STEP, fx, RS_COLOR_WRITE);
compile(Core::passWater, Shader::WATER_SIMULATE, fx, RS_COLOR_WRITE);
compile(Core::passWater, Shader::WATER_DROP, fx, RS_COLOR_WRITE);
compile(Core::passWater, Shader::WATER_RAYS, fx, RS_COLOR_WRITE | RS_DEPTH_TEST);
compile(Core::passWater, Shader::WATER_CAUSTICS, fx, RS_COLOR_WRITE);
compile(Core::passWater, Shader::WATER_COMPOSE, fx, RS_COLOR_WRITE | RS_DEPTH_TEST);
}
@@ -688,7 +689,7 @@ struct WaterCache {
vec2 s(item.size.x * DETAIL * 2.0f, item.size.z * DETAIL * 2.0f);
game->setShader(Core::passWater, Shader::WATER_STEP);
game->setShader(Core::passWater, Shader::WATER_SIMULATE);
Core::active.shader->setParam(uParam, vec4(0.995f, 1.0f, 0, Core::params.x));
Core::active.shader->setParam(uTexParam, vec4(1.0f / item.data[0]->width, 1.0f / item.data[0]->height, s.x / item.data[0]->width, s.y / item.data[0]->height));
@@ -739,9 +740,47 @@ struct WaterCache {
#endif
}
void renderRays() {
if (!visible) return;
PROFILE_MARKER("WATER_RAYS");
for (int i = 0; i < count; i++) {
Item &item = items[i];
if (!item.visible || !item.caustics) continue;
// render water plane
game->setShader(Core::passWater, Shader::WATER_RAYS);
item.caustics->bind(sReflect);
Core::ditherTex->bind(sMask);
vec3 bCenter = vec3(item.pos.x, item.pos.y + WATER_VOLUME_HEIGHT / 2, item.pos.z);
vec3 bSize = vec3(item.size.x, WATER_VOLUME_HEIGHT / 2, item.size.z);
Box box(bCenter - bSize, bCenter + bSize);
vec4 rPosScale[2] = { vec4(bCenter, 0.0), vec4(bSize, 1.0) };
Core::active.shader->setParam(uPosScale, rPosScale[0], 2);
Core::active.shader->setParam(uParam, vec4(level->rooms[item.to].getOffset(), 0.35f));
Core::setBlendMode(bmAdd);
Core::setCullMode(cmBack);
Core::setDepthWrite(false);
//Core::setDepthTest(false);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//game->getMesh()->renderBox();
game->getMesh()->renderWaterVolume(item.to);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//Core::setDepthTest(true);
Core::setDepthWrite(true);
Core::setCullMode(cmFront);
Core::setBlendMode(bmNone);
}
}
void renderMask() {
if (!visible) return;
PROFILE_MARKER("WATER_RENDER_MASK");
PROFILE_MARKER("WATER_MASK");
// mask underwater geometry by zero alpha
game->setShader(Core::passWater, Shader::WATER_MASK);
Core::active.shader->setParam(uTexParam, vec4(1.0f));
@@ -891,9 +930,9 @@ struct WaterCache {
camera->setup(true);
}
void render() {
void compose() {
if (!visible) return;
PROFILE_MARKER("WATER_RENDER");
PROFILE_MARKER("WATER_COMPOSE");
for (int i = 0; i < count; i++) {
Item &item = items[i];
if (!item.visible) continue;

View File

@@ -523,7 +523,7 @@ struct Camera : ICamera {
smooth = false;
fov = firstPerson ? 90.0f : 65.0f;
znear = firstPerson ? 8.0f : 32.0f;
znear = firstPerson ? 16.0f : 32.0f;
zfar = 45.0f * 1024.0f;
#ifdef _OS_PSP

View File

@@ -352,6 +352,7 @@ enum RenderState {
// Texture image format
enum TexFormat {
FMT_LUMINANCE,
FMT_RGBA,
FMT_RGB16,
FMT_RGBA16,
@@ -364,12 +365,13 @@ enum TexFormat {
// Texture options
enum TexOption {
OPT_CUBEMAP = 1,
OPT_MIPMAPS = 2,
OPT_NEAREST = 4,
OPT_TARGET = 8,
OPT_VERTEX = 16,
OPT_PROXY = 32,
OPT_REPEAT = 1,
OPT_CUBEMAP = 2,
OPT_MIPMAPS = 4,
OPT_NEAREST = 8,
OPT_TARGET = 16,
OPT_VERTEX = 32,
OPT_PROXY = 64,
};
// Pipeline State Object
@@ -384,6 +386,13 @@ struct PSO {
typedef uint16 Index;
struct Edge {
Index a, b;
Edge() {}
Edge(Index a, Index b) : a(a), b(b) {}
};
struct Vertex {
short4 coord; // xyz - position, w - joint index (for entities only)
short4 normal; // xyz - vertex normal, w - unused
@@ -448,8 +457,9 @@ struct MeshRange {
E( TYPE_MIRROR ) \
/* water sub-passes */ \
E( WATER_DROP ) \
E( WATER_STEP ) \
E( WATER_SIMULATE ) \
E( WATER_CAUSTICS ) \
E( WATER_RAYS ) \
E( WATER_MASK ) \
E( WATER_COMPOSE ) \
/* filter types */ \
@@ -509,7 +519,7 @@ namespace Core {
vec4 fogParams;
vec4 contacts[MAX_CONTACTS];
Texture *whiteTex, *whiteCube, *blackTex;
Texture *whiteTex, *whiteCube, *blackTex, *ditherTex;
enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass;
@@ -639,7 +649,19 @@ namespace Core {
data = 0;
blackTex = new Texture(1, 1, FMT_RGBA, OPT_NEAREST, &data);
// init settings
uint8 ditherData[] = {
0x00, 0x7F, 0x1F, 0x9F, 0x07, 0x87, 0x27, 0xA7,
0xBF, 0x3F, 0xDF, 0x5F, 0xC7, 0x47, 0xE7, 0x67,
0x2F, 0xAF, 0x0F, 0x8F, 0x37, 0xB7, 0x17, 0x97,
0xEF, 0x6F, 0xCF, 0x4F, 0xF7, 0x77, 0xD7, 0x57,
0x0B, 0x8B, 0x2B, 0xAB, 0x03, 0x83, 0x23, 0xA3,
0xCB, 0x4B, 0xEB, 0x6B, 0xC3, 0x43, 0xE3, 0x63,
0x3B, 0xBB, 0x1B, 0x9B, 0x33, 0xB3, 0x13, 0x93,
0xFB, 0x7B, 0xDB, 0x5B, 0xF3, 0x73, 0xD3, 0x53,
};
ditherTex = new Texture(8, 8, FMT_LUMINANCE, OPT_REPEAT | OPT_NEAREST, &ditherData);
// init settings
settings.version = SETTINGS_VERSION;
settings.detail.setFilter (Core::Settings::HIGH);
@@ -737,6 +759,7 @@ namespace Core {
delete whiteTex;
delete whiteCube;
delete blackTex;
delete ditherTex;
GAPI::deinit();

View File

@@ -553,8 +553,8 @@ namespace GAPI {
}
bool border = isShadow && Core::support.texBorder;
glTexParameteri(target, GL_TEXTURE_WRAP_S, border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_S, (opt & OPT_REPEAT) ? GL_REPEAT : (border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE));
glTexParameteri(target, GL_TEXTURE_WRAP_T, (opt & OPT_REPEAT) ? GL_REPEAT : (border ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE));
if (border) {
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color);
@@ -566,7 +566,8 @@ namespace GAPI {
static const struct FormatDesc {
GLuint ifmt, fmt;
GLenum type;
} formats[FMT_MAX] = {
} formats[FMT_MAX] = {
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE }, // LUMINANCE
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // RGBA
{ GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 }, // RGB16
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, // RGBA16

View File

@@ -2031,10 +2031,14 @@ struct Level : IGame {
Core::setBlendMode(bmNone);
if (water && waterCache && waterCache->visible) {
Core::Pass pass = Core::pass;
if (!camera->isUnderwater())
waterCache->renderRays();
waterCache->renderMask();
waterCache->copyScreenToRefract();
setMainLight(player);
waterCache->render();
waterCache->compose();
if (camera->isUnderwater())
waterCache->renderRays();
Core::pass = pass;
setupBinding();

View File

@@ -14,6 +14,9 @@ TR::ObjectTexture &whiteTile = barTile[4]; // BAR_WHITE
#define DOUBLE_SIDED 2
#define MAX_ROOM_DYN_FACES 512
#define WATER_VOLUME_HEIGHT (768 * 2)
#define WATER_VOLUME_OFFSET 4
struct Mesh : GAPI::Mesh {
int aIndex;
@@ -139,6 +142,7 @@ struct MeshBuilder {
Geometry geometry[3]; // opaque, double-side alpha, additive
Dynamic dynamic[3]; // lists of dynamic polygons (with animated textures) like lava, waterfalls etc.
MeshRange sprites;
MeshRange waterVolume;
int split;
} *rooms;
@@ -154,7 +158,7 @@ struct MeshBuilder {
// procedured
MeshRange shadowBlob;
MeshRange quad, circle;
MeshRange quad, circle, box;
MeshRange plane;
int transparent;
@@ -263,6 +267,28 @@ struct MeshBuilder {
iCount += CIRCLE_SEGS * 3;
vCount += CIRCLE_SEGS + 1;
// box
const Index boxIndices[] = {
2, 1, 0, 3, 2, 0,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
14, 13, 12, 15, 14, 12,
16, 17, 18, 16, 18, 19,
22, 21, 20, 23, 22, 20,
};
const short4 boxCoords[] = {
{-1, -1, 1, 0}, { 1, -1, 1, 0}, { 1, 1, 1, 0}, {-1, 1, 1, 0},
{ 1, 1, 1, 0}, { 1, 1, -1, 0}, { 1, -1, -1, 0}, { 1, -1, 1, 0},
{-1, -1, -1, 0}, { 1, -1, -1, 0}, { 1, 1, -1, 0}, {-1, 1, -1, 0},
{-1, -1, -1, 0}, {-1, -1, 1, 0}, {-1, 1, 1, 0}, {-1, 1, -1, 0},
{ 1, 1, 1, 0}, {-1, 1, 1, 0}, {-1, 1, -1, 0}, { 1, 1, -1, 0},
{-1, -1, -1, 0}, { 1, -1, -1, 0}, { 1, -1, 1, 0}, {-1, -1, 1, 0},
};
iCount += COUNT(boxIndices);
vCount += COUNT(boxCoords);
// detailed plane
#ifdef GENERATE_WATER_PLANE
iCount += SQR(PLANE_DETAIL * 2) * 6;
@@ -289,6 +315,10 @@ struct MeshBuilder {
aCount++;
}
range.waterVolume.iCount = 0;
if (Core::settings.detail.water > Core::Settings::MEDIUM)
buildWaterVolume(i, indices, vertices, iCount, vCount, vStartRoom);
for (int transp = 0; transp < 3; transp++) { // opaque, opacity
int blendMask = getBlendMask(transp);
@@ -470,12 +500,11 @@ struct MeshBuilder {
vertices[vCount + 3].texCoord = short4( 0, 0, 0, 0 );
for (int i = 0; i < 4; i++) {
Vertex &v = vertices[vCount + i];
Vertex &v = vertices[vCount++];
v.normal = short4( 0, 0, 0, 0 );
v.color = ubyte4( 255, 255, 255, 255 );
v.light = ubyte4( 255, 255, 255, 255 );
}
vCount += 4;
// circle
circle.vStart = vStartCommon;
@@ -503,6 +532,25 @@ struct MeshBuilder {
vertices[vCount + CIRCLE_SEGS].coord = short4( 0, 0, 0, 0 );
vCount += CIRCLE_SEGS + 1;
// box
box.vStart = vStartCommon;
box.iStart = iCount;
box.iCount = COUNT(boxIndices);
baseIdx = vCount - vStartCommon;
for (int i = 0; i < COUNT(boxIndices); i++)
indices[iCount++] = baseIdx + boxIndices[i];
for (int i = 0; i < COUNT(boxCoords); i++) {
Vertex &v = vertices[vCount++];
v.coord = boxCoords[i];
v.normal = short4(0, 0, 0, 32767);
v.texCoord = short4(0, 0, 0, 0);
v.color = ubyte4(255, 255, 255, 255);
v.light = ubyte4(255, 255, 255, 255);
}
// plane
#ifdef GENERATE_WATER_PLANE
plane.vStart = vStartCommon;
@@ -557,6 +605,7 @@ struct MeshBuilder {
r.geometry[j].ranges[k].aIndex = rangeRoom.aIndex;
r.sprites.aIndex = rangeRoom.aIndex;
r.waterVolume.aIndex = rangeRoom.aIndex;
}
MeshRange rangeModel;
@@ -642,7 +691,7 @@ struct MeshBuilder {
for (int i = 0; i < room.data.fCount; i++) {
TR::Face &f = room.data.faces[i];
if (f.vertices[0] == 0xFFFF) continue;
if (f.flags.value == 0xFFFF) continue;
TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex;
TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex;
@@ -663,7 +712,8 @@ struct MeshBuilder {
if (isWaterSurface(yt, s.roomAbove, room.flags.water) ||
isWaterSurface(yb, s.roomBelow, room.flags.water)) {
f.vertices[0] = 0xFFFF; // mark as unused
f.flags.value = 0xFFFF; // mark as unused
room.waterLevel = a.y;
if (f.vCount == 4) {
iCount -= 6;
@@ -672,10 +722,174 @@ struct MeshBuilder {
iCount -= 3;
vCount -= 3;
}
// preserve indices & vertices for water volume
if (room.flags.water && Core::settings.detail.water > Core::Settings::MEDIUM) {
// water volume caps
iCount += (f.vCount == 4 ? 6 : 3) * 2;
vCount += f.vCount * 2;
// water volume bounds (reserved)
iCount += 6 * f.vCount;
vCount += 4 * f.vCount;
}
}
}
}
Index addUniqueVertex(Array<TR::Vertex> &vertices, TR::Vertex &v) {
for (int i = 0; i < vertices.count; i++) {
TR::Vertex &o = vertices[i];
if (o.x == v.x && o.y == v.y && o.z == v.z)
return i;
}
return vertices.push(v);
}
void addUniqueEdge(Array<Edge> &edges, Index a, Index b) {
for (int i = 0; i < edges.count; i++) {
Edge &e = edges[i];
if ((e.a == a && e.b == b) || (e.a == b && e.b == a)) {
edges.remove(i);
return;
}
}
edges.push(Edge(a, b));
}
void buildWaterVolume(int roomIndex, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) {
TR::Room &room = level->rooms[roomIndex];
if (!room.flags.water) return;
MeshRange &range = rooms[roomIndex].waterVolume;
Array<Edge> wEdges(128);
Array<Index> wIndices(128);
Array<TR::Vertex> wVertices(128);
for (int i = 0; i < room.data.fCount; i++) {
TR::Face &f = room.data.faces[i];
if (f.flags.value != 0xFFFF) continue;
Index idx[4];
idx[0] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[0]].vertex);
idx[1] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[1]].vertex);
idx[2] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[2]].vertex);
if (f.vCount > 3) {
idx[3] = addUniqueVertex(wVertices, room.data.vertices[f.vertices[3]].vertex);
wIndices.push(idx[0]);
wIndices.push(idx[1]);
wIndices.push(idx[3]);
wIndices.push(idx[3]);
wIndices.push(idx[1]);
wIndices.push(idx[2]);
addUniqueEdge(wEdges, idx[0], idx[1]);
addUniqueEdge(wEdges, idx[1], idx[2]);
addUniqueEdge(wEdges, idx[2], idx[3]);
addUniqueEdge(wEdges, idx[3], idx[0]);
} else {
wIndices.push(idx[0]);
wIndices.push(idx[1]);
wIndices.push(idx[2]);
addUniqueEdge(wEdges, idx[0], idx[1]);
addUniqueEdge(wEdges, idx[1], idx[2]);
addUniqueEdge(wEdges, idx[2], idx[0]);
}
}
if (!wEdges.count) return;
Array<short3> wOffsets(wVertices.count);
for (int i = 0; i < wVertices.count; i++)
wOffsets.push(short3(0, WATER_VOLUME_OFFSET, 0));
for (int i = 0; i < wEdges.count; i++) {
Edge &e = wEdges[i];
TR::Vertex &a = wVertices[e.a];
TR::Vertex &b = wVertices[e.b];
int16 dx = a.z - b.z;
int16 dz = b.x - a.x;
short3 &ao = wOffsets[e.a];
ao.x = clamp(ao.x + dx, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
ao.z = clamp(ao.z + dz, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
short3 &bo = wOffsets[e.b];
bo.x = clamp(bo.x + dx, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
bo.z = clamp(bo.z + dz, -WATER_VOLUME_OFFSET, WATER_VOLUME_OFFSET);
}
range.vStart = vStart;
range.iCount = wIndices.count * 2 + wEdges.count * 6;
range.iStart = iCount;
for (int i = 0; i < wIndices.count; i += 3) {
indices[iCount++] = vCount + wIndices[i + 2];
indices[iCount++] = vCount + wIndices[i + 1];
indices[iCount++] = vCount + wIndices[i + 0];
}
for (int i = 0; i < wIndices.count; i++)
indices[iCount++] = vCount + wIndices[i] + wVertices.count;
for (int i = 0; i < wEdges.count; i++) {
Index a = wEdges[i].a;
Index b = wEdges[i].b;
indices[iCount++] = vCount + a;
indices[iCount++] = vCount + b;
indices[iCount++] = vCount + a + wVertices.count;
indices[iCount++] = vCount + b;
indices[iCount++] = vCount + b + wVertices.count;
indices[iCount++] = vCount + a + wVertices.count;
}
for (int i = 0; i < wVertices.count; i++) {
TR::Vertex &v = wVertices[i];
short3 &o = wOffsets[i];
v.x += o.x;
v.y += o.y;
v.z += o.z;
vertices[vCount++].coord = short4(v.x, v.y, v.z, 0);
}
for (int i = 0; i < wVertices.count; i++) {
TR::Vertex &v = wVertices[i];
v.y += WATER_VOLUME_HEIGHT - WATER_VOLUME_OFFSET - WATER_VOLUME_OFFSET;
const vec3 sectorOffsets[] = {
vec3(-8, 0, -8),
vec3( 8, 0, -8),
vec3( 8, 0, 8),
vec3(-8, 0, 8),
};
int16 floor = 32000;
for (int j = 0; j < 4; j++) {
vec3 pos = room.getOffset() + vec3(v.x, v.y, v.z) + sectorOffsets[j];
int16 rIndex = roomIndex;
TR::Room::Sector *sector = level->getSector(rIndex, pos);
if (sector->floor == TR::NO_FLOOR || !level->rooms[rIndex].flags.water) continue;
floor = min(floor, int16(level->getFloor(sector, pos)));
}
floor -= WATER_VOLUME_OFFSET * 3;
v.y = min(v.y, floor);
vertices[vCount++].coord = short4(v.x, v.y, v.z, 0);
}
}
inline int getBlendMask(int texAttribute) {
ASSERT(texAttribute < 3);
return 1 << texAttribute;
@@ -691,7 +905,7 @@ struct MeshBuilder {
TR::Face &f = d.faces[j];
TR::ObjectTexture &t = level.objectTextures[f.flags.texture];
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
if (f.flags.value == 0xFFFF) continue; // skip if marks as unused (removing water planes)
CHECK_ROOM_NORMAL(f);
@@ -725,7 +939,7 @@ struct MeshBuilder {
TR::Face &f = d.faces[j];
TR::ObjectTexture &t = level.objectTextures[f.flags.texture];
if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes)
if (f.flags.value == 0xFFFF) continue; // skip if marks as unused (removing water planes)
if (!(blendMask & getBlendMask(t.attribute)))
continue;
@@ -1220,6 +1434,16 @@ struct MeshBuilder {
void renderPlane() {
mesh->render(plane);
}
void renderBox() {
mesh->render(box);
}
void renderWaterVolume(int roomIndex) {
MeshRange &range = rooms[roomIndex].waterVolume;
if (range.iCount)
mesh->render(range);
}
};
#endif

View File

@@ -7,10 +7,10 @@ struct Shader : GAPI::Shader {
enum Type {
DEFAULT = 0,
/* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4,
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE = 1, FILTER_GRAYSCALE = 2, FILTER_BLUR = 3, FILTER_EQUIRECTANGULAR = 4,
/* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4,
MAX = 5
/* shader */ SPRITE = 0, FLASH, ROOM, ENTITY, MIRROR,
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE, FILTER_GRAYSCALE, FILTER_BLUR, FILTER_EQUIRECTANGULAR,
/* water */ WATER_DROP = 0, WATER_SIMULATE, WATER_CAUSTICS, WATER_RAYS, WATER_MASK, WATER_COMPOSE,
MAX = 6
};
Shader(Core::Pass pass, Type type, int *def, int defCount) : GAPI::Shader() {

View File

@@ -66,6 +66,13 @@ uniform sampler2D sNormal;
#else
gl_Position = vec4(coord.xyz, 1.0);
#endif
#ifdef WATER_RAYS
vCoord = aCoord.xyz + uParam.xyz;
vec4 cp = uViewProj * vec4(vCoord, 1.0);
vProjCoord = cp;
gl_Position = cp;
#endif
#endif
vViewVec = uViewPos.xyz - vCoord.xyz;
vLightVec = uLightPos.xyz - vCoord.xyz;
@@ -81,7 +88,7 @@ uniform sampler2D sNormal;
float calcFresnel(float VoH, float f0) {
float f = pow(1.0 - VoH, 5.0);
return f + f0 * (1.0f - f);
return f + f0 * (1.0 - f);
}
vec3 applyFog(vec3 color, vec3 fogColor, float factor) {
@@ -130,7 +137,7 @@ uniform sampler2D sNormal;
return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.0005;
}
vec4 calc() {
vec4 simulate() {
vec2 tc = vTexCoord;
if (texture2D(sMask, tc).x < 0.5)
@@ -156,6 +163,7 @@ uniform sampler2D sNormal;
return v;
}
#ifdef WATER_CAUSTICS
vec4 caustics() {
float rOldArea = length(dFdx(vOldPos)) * length(dFdy(vOldPos));
@@ -166,6 +174,45 @@ uniform sampler2D sNormal;
}
#endif
#ifdef WATER_RAYS
float boxIntersect(vec3 rayPos, vec3 rayDir, vec3 center, vec3 hsize) {
center -= rayPos;
vec3 bMin = (center - hsize) / rayDir;
vec3 bMax = (center + hsize) / rayDir;
vec3 m = min(bMin, bMax);
return max(0.0, max(m.x, max(m.y, m.z)));
}
vec4 rays() {
#define RAY_STEPS 16.0
vec3 viewVec = normalize(vViewVec);
float t = boxIntersect(uViewPos.xyz, -viewVec, uPosScale[0].xyz, uPosScale[1].xyz);
vec3 p0 = uViewPos.xyz - viewVec * t;
vec3 p1 = vCoord.xyz;
float dither = texture2D(sMask, gl_FragCoord.xy * (1.0 / 8.0)).x;
vec3 step = (p1 - p0) / RAY_STEPS;
vec3 pos = p0 + step * dither;
float sum = 0.0;
for (float i = 0.0; i < RAY_STEPS; i++) {
vec3 wpos = (pos - uPosScale[0].xyz) / uPosScale[1].xyz;
vec2 tc = wpos.xz * 0.5 + 0.5;
float light = texture2D(sReflect, tc).x;
sum += light * (1.0 - (clamp(wpos.y, -1.0, 1.0) * 0.5 + 0.5));
pos += step;
}
sum /= RAY_STEPS;
sum *= uParam.w;
return vec4(UNDERWATER_COLOR * sum, 1.0);
}
#endif
vec4 mask() {
return vec4(0.0);
}
@@ -205,14 +252,18 @@ uniform sampler2D sNormal;
return drop();
#endif
#ifdef WATER_STEP
return calc();
#ifdef WATER_SIMULATE
return simulate();
#endif
#ifdef WATER_CAUSTICS
return caustics();
#endif
#ifdef WATER_RAYS
return rays();
#endif
#ifdef WATER_MASK
return mask();
#endif

View File

@@ -1594,4 +1594,58 @@ namespace String {
}
template <typename T>
struct Array {
int capacity;
int count;
T *items;
Array(int capacity = 32) : capacity(capacity), count(0), items(NULL) {}
~Array() {
free(items);
}
int push(const T &item) {
if (!items)
items = (T*)malloc(capacity * sizeof(T));
if (count == capacity) {
capacity += capacity + capacity / 2;
if (items)
items = (T*)realloc(items, capacity * sizeof(T));
else
items = (T*)malloc(capacity * sizeof(T));
}
items[count] = item;
return count++;
}
int pop() {
ASSERT(count > 0);
return --count;
}
void removeFast(int index) {
(*this)[index] = (*this)[--count];
}
void remove(int index) {
count--;
ASSERT(count >= 0);
for (int i = index; i < count; i++)
items[i] = items[i + 1];
}
void clear() {
count = 0;
}
T& operator[] (int index) {
ASSERT(index >= 0 && index < count);
return items[index];
};
};
#endif