From fdeeb731efbbe58c780dba58ad39892c886971b9 Mon Sep 17 00:00:00 2001 From: XProger Date: Sun, 23 Sep 2018 04:48:54 +0300 Subject: [PATCH] #23 volumetric caustics effect --- src/cache.h | 49 ++++++++- src/camera.h | 2 +- src/core.h | 41 +++++-- src/gapi_gl.h | 7 +- src/level.h | 6 +- src/mesh.h | 238 +++++++++++++++++++++++++++++++++++++++-- src/shader.h | 8 +- src/shaders/water.glsl | 59 +++++++++- src/utils.h | 54 ++++++++++ 9 files changed, 430 insertions(+), 34 deletions(-) diff --git a/src/cache.h b/src/cache.h index 979137c..bfdd06a 100644 --- a/src/cache.h +++ b/src/cache.h @@ -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; diff --git a/src/camera.h b/src/camera.h index 4b3bb95..e2fa6a3 100644 --- a/src/camera.h +++ b/src/camera.h @@ -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 diff --git a/src/core.h b/src/core.h index dc26c55..19d536a 100644 --- a/src/core.h +++ b/src/core.h @@ -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(); diff --git a/src/gapi_gl.h b/src/gapi_gl.h index cf21cb2..b2668ac 100644 --- a/src/gapi_gl.h +++ b/src/gapi_gl.h @@ -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 diff --git a/src/level.h b/src/level.h index 8f43362..0b89049 100644 --- a/src/level.h +++ b/src/level.h @@ -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(); diff --git a/src/mesh.h b/src/mesh.h index 5c95d34..873c8c5 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -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 &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 &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 wEdges(128); + Array wIndices(128); + Array 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 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 diff --git a/src/shader.h b/src/shader.h index 1f971c5..26a7046 100644 --- a/src/shader.h +++ b/src/shader.h @@ -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() { diff --git a/src/shaders/water.glsl b/src/shaders/water.glsl index 65b60aa..3557d6e 100644 --- a/src/shaders/water.glsl +++ b/src/shaders/water.glsl @@ -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 diff --git a/src/utils.h b/src/utils.h index c58bd08..e4ba30a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1594,4 +1594,58 @@ namespace String { } +template +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