├── .gitignore ├── Performance Data.xlsx ├── README.md ├── README_OLD.md ├── img ├── cubepattern.png ├── cubepatternsolid.png ├── cutout.png ├── demo.aep ├── donutshatter.png ├── errors │ ├── clip.png │ ├── clip_correct.png │ └── icosphere.jpg ├── fracture1.jpg ├── fracture_demo.mp4 ├── fracturepattern.png ├── hulledges.png ├── hullpoints.png ├── manyshatter.png ├── parallelalg.png ├── parallelalg.svg ├── performance │ ├── cpu_gpu_fracture.png │ ├── cubicvr_time.png │ ├── p_s_intersection.png │ ├── stream_compact_sequential.png │ └── webcl_cpu_gpu.png ├── shadow.png ├── wallshatter.png ├── wallwireframe.png ├── wireframe.png └── youtube.png ├── src ├── CubicVR_Core.fs ├── CubicVR_Core.vs ├── cl │ └── fracture.cl ├── index.html ├── index_old.html ├── js │ ├── CubicVR.min.js │ ├── fracturecl.js │ ├── main.js │ └── ourmath.js ├── lib │ ├── CubicVR.js │ ├── ammo.fast.js │ ├── ammo.js │ ├── stats.js │ └── stats.min.js └── models │ ├── asteroids1.dae │ ├── cube.dae │ ├── cubeshatter.blend │ ├── cubeshatter.dae │ ├── donut.blend │ ├── donut.dae │ ├── icoshatter.blend │ ├── icoshatter.dae │ ├── icosphere.dae │ ├── oct.blend │ ├── oct.dae │ ├── slice.dae │ ├── tet.dae │ ├── tet1.dae │ ├── wall.blend │ ├── wall.dae │ └── wall3.dae └── testanalysis.blend /.gitignore: -------------------------------------------------------------------------------- 1 | *.blend1 2 | *.swp 3 | -------------------------------------------------------------------------------- /Performance Data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/Performance Data.xlsx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This project doesn't work anymore. Please see the [port to WebGPU](https://github.com/kainino0x/webgpu-fracture-hack). 2 | 3 | # GPU-Accelerated Dynamic Fracture in the Browser - CIS 565 Final Project 4 | 5 | Fall 2014 6 | 7 | Jiatong He, Kai Ninomiya 8 | 9 |  10 | 11 | [](https://www.youtube.com/watch?v=13sbZia4Kjc) 12 | 13 | Our goal was to create a gpu-accelerated interactive real-time mesh fracture application that runs in the browser. We used WebCL to parallelize the algorithm and CubicVR to render and simulate the rigid bodies. 14 | 15 | ~~[Live Demo](https://kainino0x.github.io/cis565final/src/): 16 | requires the [Nokia WebCL plugin](http://webcl.nokiaresearch.com/) for Firefox.~~ (EDIT 2018 Feb: WebCL is dead. In fact, it was already dead when we started this project. If you're lucky, you may still get this demo to work, but I don't recommend putting in the effort.) 17 | 18 | >**Controls** 19 | > 20 | >`click + drag` : Rotate camera view 21 | > 22 | >`alt + click + drag` : Pan camera view 23 | > 24 | >`mouse scroll` : Zoom camera 25 | > 26 | >`click on object + drag` : Move selected object around 27 | > 28 | >`click + drag` : Rotate camera view 29 | > 30 | >`F + click on object` : Fracture object 31 | > 32 | >`W + click` : Toggle wireframe 33 | > 34 | >`D + click` : Toggle fracture pattern 35 | 36 | _Based on 37 | [Real Time Dynamic Fracture with Volumetric Approximate Convex Decompositions](https://www.graphics.rwth-aachen.de/media/teaching_files/mueller_siggraph12.pdf) 38 | by Müller, Chentanez, and Kim._ 39 | 40 | ## Table of Contents 41 | * [Algorithm Overview](#algorithm-overview) 42 | * [Fracturing](#fracturing) 43 | * [Alignment](#alignment) 44 | * [Intersection](#intersection) 45 | * [Welding](#welding) 46 | * [Island Detection](#island-detection) 47 | * [Partial Fracture](#partial-fracture) 48 | * [Implementation Details](#implementation-details) 49 | * [Fracturing](#fracturing-1) 50 | * [Intersection](#intersection-1) 51 | * [Stream Compaction](#stream-compaction) 52 | * [Partial Fracture](#partial-fracture-1) 53 | * [Working with WebCL](#working-with-webcl) 54 | * [WebCL Performance Issues](#webcl-performance-issues) 55 | * [Integration into an Existing Renderer/Rigid Body Simulator](#integration-into-an-existing-rendererrigid-body-simulator-cubicvr) 56 | * [Performance Analysis](#performance-analysis) 57 | * [Fracture Performance](#fracture-performance) 58 | * [Intersection: GPU vs. CPU, Parallel vs. Sequential](#intersection-gpu-vs-cpu-parallel-vs-sequential) 59 | * [Stream Compaction: Parallel vs. Sequential](#stream-compaction-parallel-vs-sequential) 60 | * [WebCL Performance](#webcl-performance) 61 | * [CubicVR's Limits in Real-Time Simulation](#cubicvrs-limits-in-real-time-simulation) 62 | 63 | ## Algorithm Overview 64 | ### Fracturing 65 | At a high level, fracturing is implemented by performing boolean intersection 66 | between segments of a fracture pattern and the segments of the object to be 67 | fractured. The fracture pattern can be pre-generated, as is the case for our implementation. 68 | 69 |  70 | 71 | _A pre-generated test fracture pattern (series of solid meshes):_ 72 | 73 | #### Alignment 74 | The first step is to align the fracture pattern with the point of impact. 75 | 76 |  77 | 78 | _The wireframe is the impacted mesh, the solid is the fracture pattern_ 79 | 80 | We use the point the user clicks on the object as the point of impact, and transform the fracture mesh appropriately (all meshes are centered at 0,0,0). 81 | 82 | #### Intersection 83 | The mesh must then be intersected with the fracture mesh, resulting in one shard per cell of the fracture pattern. A simple way to do this is to clip the mesh against each face of the cell, for each cell in the fracture pattern. 84 | 85 |  86 | 87 | _A solid render of the 3D fracture pattern we are using_ 88 | 89 | #### Welding* 90 | If a shard completely fills a cell, then it can be replaced with the cell's geometry. This reduces the number of triangles produced by the intersection. 91 | 92 | #### Island Detection* 93 | If a clipping cell results in disconnected pieces within a cell, island detection should be used to split those pieces into multiple shards, instead of just one. That way you won't have disconnected pieces moving together as though they were one mesh. 94 | 95 | ### Partial Fracture 96 | Partial fracture occurs if we limit the area of effect of the fracture to some distance around the point of impact. 97 | 98 |  99 | 100 | _Notice how the bottom and right sides of the wall stay intact while the upper-left is fractured_ 101 | 102 | Rather than allowing the entire mesh to be fractured, we only fully shard the cells within the area of effect. Shards in cells outside of the area of effect can be merged together back into a single mesh. 103 | 104 | _\* : not implemented._ 105 | 106 | ## Implementation Details 107 |  108 | 109 | _A fractured torus_ 110 | 111 | ### Fracturing 112 | The fracturing algorithm was our greatest challenge. It's an algorithm that is naturally sequential--clipping polygons usually requires some knowledge of neighbors and other information. However, we devised a method that successfully targets independent pieces of the algorithm at the cost of some accuracy. 113 | #### Intersection 114 | Our intersection algorithm is simple clipping planes. For each cell, the mesh is clipped by each cell face to give us the shard. What's interesting is how we parallelized it. 115 | ##### Parallelization 116 | Our strategy for the parallelization of the intersection was to treat the mesh as a set of disconnected triangles. By doing so, we could parallelize by-cell-by-triangle. 117 | 118 |  119 | 120 | _Diagram of the parallel algorithm we used to clip meshes_ 121 | 122 | For each face of the cell, we clip each triangle in the mesh by that face independently, then create the new faces for them. We can process all cells at once, and iterate a total number of times equal to the maximum number of faces in a single cell. 123 | 124 | #### Stream Compaction 125 | Our implementation uses stream compaction to remove culled triangles each iteration in order to keep the number of triangles under control (otherwise it could grow at a rate of 2^n). We tried both a sequential and a parallel version of this algorithm to see which one was better. The sequential implementation simply iterates through the list and pushes non-culled objects into a new array. 126 | ##### Parallelization 127 | The reason we wanted to do stream compaction on the GPU was to reduce the amount of memory transfer between CPU and GPU. Each time our plane clipping kernel returned, we would need to copy the entire output back onto the CPU, remove bad values, add new faces, and put everything back into the GPU. If stream compaction were parallelized, we would not have that problem. 128 | 129 | We implemented stream compaction in WebCL, but ran into some performance issues that made it much slower than the copy+process on CPU method. As a result, we abandoned the stream compaction (the code is still in the stream-compaction branch) and are now removing bad values sequentially. The performance analysis section furhter below contains more details about this issue. 130 | 131 | #### Partial Fracture 132 | This feature is noteworthy because we technically cheated this one. Instead of properly combining faces, or doing some processing, we just group all the fragments that are not in the area of effect into a single mesh. This means that said mesh will have: 1, several times more geometry than other fragments, 2, faces inside of the mesh, and 3, slightly overlapping/disconnected edges on the surface. 133 | 134 |  135 | 136 | _The body on the upper-left is the merged mesh. See how its individual components are clearly visible in the wireframe?_ 137 | 138 | ### Working with WebCL 139 | Because our target was an in-browser experience, we were limited to two choices for GPU-acceleration: WebGL and WebCL. While WebGL runs natively in most browsers, it does not yet support compute shaders as of this time, so we would have had to hack a solution using textures and feedbacks. WebCL, on the other hand, is supported by **no** browsers, but Nokia has a plugin that can run it. We chose to use WebCL for its flexibility compared to WebGL. 140 | #### WebCL Performance Issues 141 | We did, however, run into some performance issues with WebCL that were severe enough that a GPU stream compaction was slower than a sequential javascript method. You can see a comparison between the two in the Performance Analysis section. In addition, we logged the runtimes of individual set args, read/write, and kernel calls to show how slow it actually is. 142 | 143 | ### Integration into an Existing Renderer/Rigid Body Simulator (CubicVR) 144 | Because our main focus was creating the fractured geometry, we looked for an existing renderer and rigid body simulator. CubicVR (physics built on ammo.js, which is compiled from bullet) provides a very simple-to-use library for both, though we ran into some issues here as well. The performance issues we had with usingCubicVR are detailed in the Performance Analysis section of the readme. 145 | 146 | 147 | ## Performance Analysis 148 | 149 | Performance measurements were taken on one of the following setups. (Performance comparisons are on one machine.) 150 | 151 | * Arch Linux, Intel i5-4670 + NVIDIA GTX 750 (CPU/GPU comparisons) 152 | * Windows 8.1, Intel i7-4700HQ (CPU-only measurements) 153 | 154 | ### Fracture Performance 155 | We began to implement fracture using a naive sequential algorithm as a proof-of-concept. The algorithm runs somewhat differently compared to the parallel algorithm (it sequentially does the clipping planes on the entire mesh, and keeps the mesh closed), but it's nice as a basis of comparison. 156 | #### Intersection: GPU vs. CPU, Parallel vs. Sequential 157 | We compared the runtime of our code on the GPU vs. the CPU, as well as the parallel and sequential implementations of the intersection. 158 | 159 |  160 | 161 | _Runtime of the fracture algorithm on the CPU compared to the GPU_ 162 | 163 | The main thing to notice here is that the CPU and GPU times are nearly identical. We have no explanation for why reads/writes have the same runtime, as well as kernels. It may just be an issue with a data set that's too small. 164 | 165 |  166 | 167 | _Runtime of the parallel algorithm compared a naive sequential version_ 168 | 169 | Ignoring the part of the parallel graph where it starts to dip down, it's generally clear that the parallel algorithm does not scale very quickly with increasing triangle count, while the sequential algorithm begins taking far more time with each step. While this graph does not show it, the sequential implementation quickly surpasses the parallel implementation in runtime with higher number of triangles, as expected. 170 | 171 | 172 | ### Stream Compaction: Parallel vs. Sequential 173 | Each time we clip a plane-per-cell from the set of triangles, we're left with a lot of culled triangles that can be removed to keep memory costs low. We implemented both a sequential remove (iterating through and adding valid triangles to another array) and a parallel stream compaction. The sequential remove requires copying memory back onto the CPU, then performing the remove in javascript, while the stream compaction keeps everything on the GPU. 174 | 175 |  176 | 177 | _Runtime of the stream compaction algorithm vs. a sequential remove_ 178 | 179 | Strangely enough, the stream compaction (though a naive implementation) performs far worse than the sequential remove, which requires a lot of back-and-forth between the CPU and GPU. We weren't sure what was causing this, so we investigated the performance of WebCL to see if that was the issue (Stream Compaction makes a LOT of webCL calls). 180 | 181 | ### WebCL Performance 182 | 183 |  184 | 185 | _WebCL runtimes on the GPU and CPU. Note how they are nearly identical_ 186 | 187 | As expected from the Intersection performance analysis, the GPU and CPU calls are actually nearly identical. The only possible error is in the "Run Kernel" values in each, since they may be affected by our worksize setup. The runtimes of each of the other calls average to around 0.8ms each. This quickly adds up when you need to make somee 50 of these calls for each face-per-cell, along with running the actual kernel. We suspect that the poor performance of the parallel algorithms are due to this slower runtime. 188 | 189 | ### CubicVR's Limits in Real-Time Simulation 190 | 191 | CubicVR posed some challenges to our simulation, specifically in how our fragmented shards were added to the scene. The main factor is computing the collision detection surface on each of the shards. Using a convex hull collision, the processing time from CubicVR was far higher (~80% of the total time) than the time taken during our algorithm. However, convex hull collisions ran quickly in the simulator. In order to prevent long freezes, we switched over to mesh collision ang got the following results: 192 | 193 |  194 | 195 | _CubicVR's contribution to the total time of our algorithm_ 196 | 197 | Now CubicVR is no longer the source of the bottleneck of our algorithm, but mesh collision runs much more slowly than convex hull collision. 198 | 199 | The issue here is the large number of triangles we are generating. Convex hulls take a long time to calculate for meshes with large triangle counts. We can reduce the calculation time by implementing optimizations that reduce the geometry for each shard (they end up with far more faces than is necessar) such as the "welding" step. 200 | 201 | ## References 202 | 203 | [1] Matthias Müller, Nuttapong Chentanez, and Tae-Yong Kim. 2013. 204 | Real time dynamic fracture with volumetric approximate convex decompositions. 205 | ACM Trans. Graph. 32, 4, Article 115 (July 2013), 10 pages. 206 | DOI=10.1145/2461912.2461934 http://doi.acm.org/10.1145/2461912.2461934 207 | 208 | [2] stats.js. Copyright 2009-2012 Mr.doob. Used under MIT License. 209 | https://github.com/mrdoob/stats.js 210 | 211 | [3] CubicVR 3D Engine. Javascript Port of the CubicVR 3D Engine by Charles J. Cliffe. 212 | Used under MIT License. 213 | https://github.com/cjcliffe/CubicVR.js 214 | 215 | [4] ammo.js. A direct port of the Bullet physics engine to JavaScript, using Emscripten. 216 | Used under zlib License. 217 | https://github.com/kripken/ammo.js 218 | -------------------------------------------------------------------------------- /README_OLD.md: -------------------------------------------------------------------------------- 1 | CIS565: Final Project -- GPU-Accelerated Dynamic Real-Time Fracture in the Browser 2 | =========== 3 | Fall 2014 4 | ----------- 5 | Jiatong He, Kai Ninomiya 6 | ----------- 7 | 8 | [Live demo](https://kainino0x.github.io/cis565final/src/): 9 | requires the [Nokia WebCL plugin](http://webcl.nokiaresearch.com/) for Firefox. 10 | 11 | Based on 12 | [Real Time Dynamic Fracture with Volumetric Approximate Convex Decompositions](https://www.graphics.rwth-aachen.de/media/teaching_files/mueller_siggraph12.pdf) 13 | by Müller, Chentanez, and Kim. 14 | 15 | Algorithm Overview 16 | ----------------- 17 | ###Fracturing 18 | At a high level, fracturing is implemented by performing boolean intersection 19 | between segments of a fracture pattern and the segments of the object to be 20 | fractured. More information on the method is found in Mueller's paper (above). 21 | 22 | A pre-generated test fracture pattern (series of solid meshes): 23 | 24 |  25 | 26 | ##Checklist 27 | ####Part 1 28 | * Set up codebase (Turbulenz?) 29 | * Get a working rigid body simulator 30 | * Integrate simple webCL (eg. shift mesh's colors) 31 | * Generate some 3D Voronoi decomposition 32 | * Find and highlight intersections with simple mesh (eg glass panel) 33 | 34 | ##TODO 35 | * ~~Parallel implementation of plane-triangle clipping.~~ 36 | * ~~use webCL to create a copy of the input mesh for each fracture cell.~~ 37 | * webCL implementation of stream compaction (used in intersection). 38 | * webCL implementation of makeFaces() (needs webCL scan). 39 | * ~~correct orientation/center of fracture pieces.~~ 40 | * ~~center the fracture pattern on point of impact.~~ 41 | * ~~sew fractured pieces back together based on proximity to point of impact.~~ 42 | * scale fracture patterns up to the mesh geometry(or generate sufficiently large ones) (or generate one with a single outer cell whose fac normals all point outwards?) 43 | * ~~Find the correct center of meshes (for better RBD in bullet)~~ 44 | * correctly handle concave surfaces (island detection). 45 | * ~~Modify linear velocity based on direction and distance from impact point.~~ 46 | * Optimization: Welding step (replace fragments with cell mesh if the fragment completely fills the cell). 47 | * Optimization: detecting collinear edges and merging their faces 48 | * performance testing, profiling, other optimizations. 49 | 50 | ##Progress 51 | ###Part 1 52 | ####Setting up the codebase 53 | We initially began with Turbulenz, since it seemed like the most powerful engine available and combined the rigid body physics we needed with a renderer. However, it was difficult to work with the convex hulls we wanted, so we switched to CubicVR.js, an open-source 3d engine that uses ammo.js for physics. 54 | 55 | Based on our experience so far, we will probably switch to using mesh 56 | representations instead of specialized convex hull representations, due to the 57 | more useful properties of meshes. Given this switch, it's possible we'll also 58 | move back to Turbulenz or another engine. 59 | 60 | ####Working Rigid Body Simulator 61 | This was very easy to set up with CubicVR/Ammo (using one of the CubicVR 62 | provided examples), and was completed quickly. 63 | 64 | ####WebCL 65 | Currently not implemented due to our focus on getting a working engine and demo 66 | 67 | ####3D Voronoi Decomposition as fracture pattern 68 | This one turned out to be a little tricky because we didn't find a javascript implementation of 3d voronoi decomposition. We plan on making a library for it later, but for testing, we are using a voronoi decomposition made from Blender. Blender has the ability to generate a voronoi decomposition from a set of points, which is good enough for us to use as a constant, pregenerated fracture pattern. This is saved to a .dae file and read in as a set of meshes (cells). 69 | 70 | ####Intersection Testing 71 | Intersection testing was difficult to solve because we were somewhat limited by the structures available to us through the CubicVR engine. We spent some time figuring out the method to handle the cell-mesh intersections, which was more difficult than expected due to some limits in the libraries we used. 72 | 73 | The most significant one is that Bullet's btConvexHullComputer, which computes a convex hull mesh based on a set of points, seems to be unavailable in ammo.js. 74 | This means that we would need to calculate our own convex hull mesh, or work entirely in convex hulls, which would result in some necessary approximations for the geometry. 75 | 76 | Due to these limitations with the convex hull data exposed by Ammo.js, our next 77 | approach will be to do our operations directly on the meshes. In order to move 78 | forward, we plan to perform the fracture computations directly on mesh data 79 | (either an existing CubicVR structure or another simple implementation). 80 | 81 | ##Debug images 82 | 83 | The set of points on the the original object 84 | (which is all that the btConvexHullShape stores): 85 | 86 |  87 | 88 | The set of edges returned by the Bullet btConvexHullShape object 89 | (also not so useful!): 90 | 91 |  92 | 93 | ##Paralelization of the Intersection Algorithm 94 | ###Initial Thoughts 95 | The intersection algorithm is the first point of paralelization. Each fracture cell can be processed onto the mesh independently, which means that we can first paralelize by cell. Within each cell, each plane of the cell needs to be used as a clipping plane to then clip against the mesh. This will likely be a bottleneck for the code, since clipping planes will need to be run sequentially. 96 | 97 | Note that the current algorithm assumes a triangulated mesh. 98 | 99 | The intersection algorithm outline looks to be as follows: 100 | 101 | ``` 102 | setupClipMesh(); 103 | 104 | For each fracture cell: 105 | For each face of the fracture cell: 106 | // perform clipping plane algorithm. Based on the algorithm described at 107 | // http://www.geometrictools.com/Documentation/ClipMesh.pdf 108 | clipVertices(); // mark vertices as culled or passed. 109 | clipEdges(); // mark edges as culled, intersecting, or passed. 110 | clipFaces(); // mark faces as culled or passed, update faces based on edges. Create new faces/edges if necessary to maintain triangulation. 111 | end for 112 | end for 113 | 114 | rebuildMeshes(); // Parse through the vertex and face arrays and keep the unclipped ones, updating indices. 115 | ``` 116 | 117 | clipFaces() is a little more work than the others. The core of the algorithm is: 118 | * Update the faces whose edges are partially clipped. 119 | * Generate new edges along the plane 120 | * Generate faces to cover those new edges. This face will be coplanar to the clipping plane. 121 | 122 | There are four cases that can occur for faces: 123 | * 1. all edges are above the clipping plane (no change). 124 | * 2. all edges are below the clipping plane (face gets removed). 125 | * 3. one edge is above, two intersect the clipping plane (this intersection forms a quad so we need to create two new edges and one new face). 126 | * 4. one edge is below, two intersect the clipping plane (this intersection forms a triangle so we only need to add in one new edge). 127 | 128 | Some issues are immediately clear: 129 | * Different amounts of processing will be required for different cells/planes. This varies depending primarily on the number of faces in a cell. 130 | * Different amounts of memory will be needed for different cells/planes. It is not immediately clear how many new vertices and faces a clipping plane will create. Do we need to overcompensate and assume the worst-case scenario every time? This will likely become the main concern. 131 | * We need to adapt the presented algorithm to be able to handle multiple sequential clipping planes. This should be straightforward since new vertices, edges, and faces are added into the list. 132 | * What happens when the fracture pattern does not completely cover the mesh? do we scale it up so that it encloses the bounding box? Do we need to handle outside pieces in a special way? 133 | 134 | Some optimizations can be made: 135 | * The fracture pattern can be stored as a non-triangulated mesh. That is, each face is in its own plane. This way there is no need to calculate the minimum number of clipping planes necessary. 136 | * What if we optimized by vertex-face-edge : plane intersection as opposed to cell-by-cell? 137 | 138 | Keeping these issues in mind, we came up with a new way to parallelize the intersection algorithm: 139 | ###Chosen Algorithm 140 | The goal of this algorithm was to find a method to clip meshes that had the least amount of dependence between units. With this in mind, we came up with the following: 141 | 142 | Our code now parallelizes on a per-clipping-face-per-mesh-triangle level. We run one clipping plane per cell on each triangle in the mesh per loop iteration, iterating through the list of clipping planes per cell. 143 | 144 | One key feature is that we no longer handle the mesh as a whole (we still keep track), but as a set of unrelated triangles. We take this triangle "soup" and run it through the algorithm, getting new triangles with each iteration. These triangles can be connected by merging identical points at the end of the algorithm if a closed mesh is desired, but disconnected triangles works for our purposes. 145 | 146 | The loop runs for max(#cellfaces) iterations. The kernel processes a triangle-clipping plane pair with a cell number attached to it, and returns a list of triangles and a list of new points. We take this list and generate a set of new triangles to add to the list, and reiterate. 147 | 148 | ###Concave Plane Intersections 149 | How to handle? Centroid connection does not work! 150 | 151 | Edge loops? 152 | 153 | ##Stream Compaction 154 | In order to reduce gpu-cpu memory transfer. 155 | 156 | ##Island Detection 157 | Low priority, necessary both before and after partial fracture. If implemented, then partial fracture needs to be in its own kernel or loop. 158 | 159 | ##Partial Fracture 160 | ###Algorithm: 161 | * label each cell as "affected" or "not affected" 162 | * after the intersection algorithm runs, combine all "not affected" fracture pieces into a single mesh (for our purposes, put them all into a single cell) 163 | * (optional) perform island detection on that mesh 164 | -------------------------------------------------------------------------------- /img/cubepattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/cubepattern.png -------------------------------------------------------------------------------- /img/cubepatternsolid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/cubepatternsolid.png -------------------------------------------------------------------------------- /img/cutout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/cutout.png -------------------------------------------------------------------------------- /img/demo.aep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/demo.aep -------------------------------------------------------------------------------- /img/donutshatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/donutshatter.png -------------------------------------------------------------------------------- /img/errors/clip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/errors/clip.png -------------------------------------------------------------------------------- /img/errors/clip_correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/errors/clip_correct.png -------------------------------------------------------------------------------- /img/errors/icosphere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/errors/icosphere.jpg -------------------------------------------------------------------------------- /img/fracture1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/fracture1.jpg -------------------------------------------------------------------------------- /img/fracture_demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/fracture_demo.mp4 -------------------------------------------------------------------------------- /img/fracturepattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/fracturepattern.png -------------------------------------------------------------------------------- /img/hulledges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/hulledges.png -------------------------------------------------------------------------------- /img/hullpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/hullpoints.png -------------------------------------------------------------------------------- /img/manyshatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/manyshatter.png -------------------------------------------------------------------------------- /img/parallelalg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/parallelalg.png -------------------------------------------------------------------------------- /img/parallelalg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 546 | -------------------------------------------------------------------------------- /img/performance/cpu_gpu_fracture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/performance/cpu_gpu_fracture.png -------------------------------------------------------------------------------- /img/performance/cubicvr_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/performance/cubicvr_time.png -------------------------------------------------------------------------------- /img/performance/p_s_intersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/performance/p_s_intersection.png -------------------------------------------------------------------------------- /img/performance/stream_compact_sequential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/performance/stream_compact_sequential.png -------------------------------------------------------------------------------- /img/performance/webcl_cpu_gpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/performance/webcl_cpu_gpu.png -------------------------------------------------------------------------------- /img/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/shadow.png -------------------------------------------------------------------------------- /img/wallshatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/wallshatter.png -------------------------------------------------------------------------------- /img/wallwireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/wallwireframe.png -------------------------------------------------------------------------------- /img/wireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/wireframe.png -------------------------------------------------------------------------------- /img/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kainino0x/cis565final/708780b9c9d99b1a93e74d798b36442ca80bd406/img/youtube.png -------------------------------------------------------------------------------- /src/CubicVR_Core.fs: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | #if LIGHT_PERPIXEL 3 | precision mediump float; 4 | #else 5 | precision lowp float; 6 | #endif 7 | #endif 8 | 9 | #if FOG_ENABLED 10 | uniform vec3 fogColor; 11 | uniform float fogDensity; 12 | 13 | uniform float fogNear; 14 | uniform float fogFar; 15 | #endif 16 | 17 | uniform vec3 materialAmbient; 18 | uniform vec3 lightAmbient; 19 | uniform vec3 materialColor; 20 | 21 | #if POINT_SIZE && !POINT_SPRITE && POINT_CIRCLE 22 | varying float ptSize; 23 | varying vec2 sPos; 24 | #endif 25 | 26 | #if LIGHT_PERPIXEL 27 | 28 | uniform vec3 materialDiffuse; 29 | uniform vec3 materialSpecular; 30 | uniform float materialShininess; 31 | 32 | #if LIGHT_IS_POINT||LIGHT_IS_DIRECTIONAL||LIGHT_IS_SPOT||LIGHT_IS_AREA 33 | uniform vec3 lightDirection[LIGHT_COUNT]; 34 | uniform vec3 lightPosition[LIGHT_COUNT]; 35 | uniform vec3 lightSpecular[LIGHT_COUNT]; 36 | uniform vec3 lightDiffuse[LIGHT_COUNT]; 37 | uniform float lightIntensity[LIGHT_COUNT]; 38 | uniform float lightDistance[LIGHT_COUNT]; 39 | #if LIGHT_IS_SPOT 40 | uniform float lightCutOffAngle[LIGHT_COUNT]; 41 | #endif 42 | #endif 43 | 44 | #if LIGHT_IS_PROJECTOR 45 | uniform sampler2D lightProjectionMap[LIGHT_COUNT]; 46 | #endif 47 | 48 | #if LIGHT_SHADOWED 49 | varying vec4 lightProjectionOut[LIGHT_COUNT]; 50 | uniform sampler2D lightShadowMap[LIGHT_COUNT]; 51 | uniform vec3 lightDepthClip[LIGHT_COUNT]; 52 | #endif 53 | 54 | #else // !LIGHT_PERPIXEL 55 | varying vec3 lightColorOut; 56 | varying vec3 lightSpecularOut; 57 | 58 | #endif // LIGHT_PERPIXEL 59 | 60 | varying vec3 vertexNormalOut; 61 | varying vec2 vertexTexCoordOut; 62 | 63 | #if VERTEX_COLOR 64 | varying vec3 vertexColorOut; 65 | #endif 66 | 67 | #if FX_DEPTH_ALPHA||LIGHT_DEPTH_PASS||LIGHT_SHADOWED 68 | 69 | uniform vec3 postDepthInfo; 70 | float ConvertDepth3(float d) { return (postDepthInfo.x*postDepthInfo.y)/(postDepthInfo.y-d*(postDepthInfo.y-postDepthInfo.x)); } 71 | // transform range in world-z to 0-1 for near-far 72 | float DepthRange( float d ) { return ( d - postDepthInfo.x ) / ( postDepthInfo.y - postDepthInfo.x ); } 73 | 74 | float ConvertDepth3A(float d, float near, float far) { return (near*far)/(far-d*(far-near)); } 75 | // transform range in world-z to 0-1 for near-far 76 | float DepthRangeA( float d, float near, float far ) { return ( d - near ) / ( far - near ); } 77 | #endif 78 | 79 | #if LIGHT_DEPTH_PASS 80 | vec4 packFloatToVec4i(const float value) 81 | { 82 | const vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0); 83 | const vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0); 84 | vec4 res = fract(value * bitSh); 85 | res -= res.xxyz * bitMsk; 86 | return res; 87 | } 88 | 89 | #endif 90 | 91 | #if LIGHT_SHADOWED 92 | float unpackFloatFromVec4i(const vec4 value) 93 | { 94 | const vec4 bitSh = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0); 95 | return(dot(value, bitSh)); 96 | } 97 | 98 | #if LIGHT_SHADOWED_SOFT 99 | float getShadowVal(sampler2D shadowTex,vec4 shadowCoord, float proj, float texel_size) { 100 | vec2 filterTaps[6]; 101 | filterTaps[0] = vec2(-0.326212,-0.40581); 102 | filterTaps[1] = vec2(-0.840144,-0.07358); 103 | filterTaps[2] = vec2(-0.695914,0.457137); 104 | filterTaps[3] = vec2(-0.203345,0.620716); 105 | filterTaps[4] = vec2(0.96234,-0.194983); 106 | filterTaps[5] = vec2(0.473434,-0.480026); 107 | 108 | /* filterTaps[6] = vec2(0.519456,0.767022); 109 | filterTaps[7] = vec2(0.185461,-0.893124); 110 | filterTaps[8] = vec2(0.507431,0.064425); 111 | filterTaps[9] = vec2(0.89642,0.412458) ; 112 | filterTaps[10] =vec2(-0.32194,-0.932615); 113 | filterTaps[11] =vec2(-0.791559,-0.59771); */ 114 | 115 | float shadow = 0.0; 116 | vec4 shadowSample; 117 | float distanceFromLight; 118 | 119 | for (int i = 0; i < 6; i++) { 120 | shadowSample = texture2D(shadowTex,shadowCoord.st+filterTaps[i]*(2.0*texel_size)); 121 | 122 | distanceFromLight = unpackFloatFromVec4i(shadowSample); 123 | 124 | shadow += distanceFromLight <= shadowCoord.z ? 0.0 : 1.0 ; 125 | } 126 | 127 | shadow /= 6.0; 128 | 129 | return shadow; 130 | } 131 | #else 132 | float getShadowVal(sampler2D shadowTex,vec4 shadowCoord, float proj, float texel_size) { 133 | vec4 shadowSample = texture2D(shadowTex,shadowCoord.st); 134 | 135 | float distanceFromLight = unpackFloatFromVec4i(shadowSample); 136 | float shadow = 1.0; 137 | 138 | shadow = distanceFromLight <= (shadowCoord.z) ? 0.0 : 1.0 ; 139 | 140 | return shadow; 141 | } 142 | #endif 143 | #endif 144 | 145 | #if !LIGHT_DEPTH_PASS 146 | #if TEXTURE_COLOR 147 | uniform sampler2D textureColor; 148 | #endif 149 | 150 | #if TEXTURE_BUMP||TEXTURE_NORMAL 151 | varying vec3 envEyeVectorOut; 152 | #endif 153 | #if TEXTURE_BUMP 154 | uniform sampler2D textureBump; 155 | #endif 156 | 157 | 158 | #if TEXTURE_ENVSPHERE 159 | uniform sampler2D textureEnvSphere; 160 | uniform float materialEnvironment; 161 | #if TEXTURE_NORMAL 162 | varying vec3 envTexCoordOut; 163 | #else 164 | varying vec2 envTexCoordOut; 165 | #endif 166 | #endif 167 | 168 | #if TEXTURE_REFLECT 169 | uniform sampler2D textureReflect; 170 | #endif 171 | 172 | #if TEXTURE_NORMAL 173 | uniform sampler2D textureNormal; 174 | #endif 175 | 176 | uniform float materialAlpha; 177 | 178 | #if TEXTURE_AMBIENT 179 | uniform sampler2D textureAmbient; 180 | #endif 181 | 182 | #if TEXTURE_SPECULAR 183 | uniform sampler2D textureSpecular; 184 | #endif 185 | 186 | #endif // !LIGHT_DEPTH_PASS 187 | 188 | #if TEXTURE_ALPHA 189 | uniform sampler2D textureAlpha; 190 | #endif 191 | 192 | varying vec4 vertexPositionOut; 193 | 194 | vec2 cubicvr_texCoord() { 195 | #if LIGHT_DEPTH_PASS 196 | return vertexTexCoordOut; 197 | #else 198 | #if POINT_SPRITE 199 | return gl_PointCoord; 200 | #else 201 | #if TEXTURE_BUMP 202 | float height = texture2D(textureBump, vertexTexCoordOut.xy).r; 203 | float v = (height) * 0.05 - 0.04; // * scale and - bias 204 | vec3 eye = normalize(envEyeVectorOut); 205 | return vertexTexCoordOut.xy + (eye.xy * v); 206 | #else 207 | return vertexTexCoordOut; 208 | #endif 209 | #endif 210 | #endif 211 | } 212 | 213 | #if TEXTURE_NORMAL && OES_STANDARD_DERIVATIVES 214 | 215 | #extension GL_OES_standard_derivatives : enable 216 | 217 | // Normal Mapping Without Precomputed Tangents: http://www.thetenthplanet.de/archives/1180 218 | 219 | mat3 cotangent_frame( vec3 N, vec3 p, vec2 uv ) { 220 | // get edge vectors of the pixel triangle 221 | vec3 dp1 = dFdx( p ); 222 | vec3 dp2 = dFdy( p ); 223 | vec2 duv1 = dFdx( uv ); 224 | vec2 duv2 = dFdy( uv ); 225 | 226 | // solve the linear system 227 | vec3 dp2perp = cross( dp2, N ); 228 | vec3 dp1perp = cross( N, dp1 ); 229 | vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; 230 | vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; 231 | 232 | // construct a scale-invariant frame 233 | float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) ); 234 | return mat3( T * invmax, B * invmax, N ); 235 | } 236 | 237 | #define WITH_NORMALMAP_UNSIGNED 1 238 | //#define WITH_NORMALMAP_GREEN_UP 1 239 | //#define WITH_NORMAL_2CHANNEL 1 240 | 241 | vec3 perturb_normal( vec3 N, vec3 V, vec2 texCoord ) { 242 | // assume N, the interpolated vertex normal and 243 | // V, the view vector (vertex to eye) 244 | vec3 map = texture2D( textureNormal, texCoord ).xyz; 245 | #ifdef WITH_NORMALMAP_UNSIGNED 246 | map = map * 255./127. - 128./127.; 247 | #endif 248 | #ifdef WITH_NORMALMAP_2CHANNEL 249 | map.z = sqrt( 1. - dot( map.xy, map.xy ) ); 250 | #endif 251 | #ifdef WITH_NORMALMAP_GREEN_UP 252 | map.y = -map.y; 253 | #endif 254 | mat3 TBN = cotangent_frame( N, -V, texCoord ); 255 | return normalize( TBN * map ); 256 | } 257 | 258 | #endif 259 | 260 | 261 | vec3 cubicvr_normal(vec2 texCoord) { 262 | #if TEXTURE_NORMAL && !LIGHT_DEPTH_PASS 263 | 264 | // use standard derivatives version if available 265 | #if OES_STANDARD_DERIVATIVES 266 | return perturb_normal(vertexNormalOut, vertexPositionOut.xyz, texCoord); 267 | #else 268 | // fake it otherwise, doesn't play well with rotation 269 | vec3 bumpNorm = vec3(texture2D(textureNormal, texCoord)); 270 | 271 | vec3 n = (vec4(normalize(vertexNormalOut),1.0)).xyz; 272 | bumpNorm = (bumpNorm-0.5)*2.0; 273 | bumpNorm.y = -bumpNorm.y; 274 | return normalize((n+bumpNorm)/2.0); 275 | #endif 276 | 277 | #else 278 | return normalize(vertexNormalOut); 279 | #endif 280 | } 281 | 282 | #if FOG_ENABLED 283 | vec4 apply_fog(vec4 color) { 284 | vec4 outColor = color; 285 | 286 | float depth = gl_FragCoord.z / gl_FragCoord.w; 287 | 288 | #if USE_FOG_EXP 289 | const float LOG2 = 1.442695; 290 | float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 ); 291 | fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 ); 292 | outColor = mix( color, vec4( fogColor, color.w ), fogFactor ); 293 | #endif 294 | 295 | #if USE_FOG_LINEAR 296 | float fogFactor = smoothstep( fogNear, fogFar, depth ); 297 | outColor = mix( color, vec4( fogColor, color.w ), fogFactor ); 298 | #endif 299 | 300 | return outColor; 301 | } 302 | #endif 303 | 304 | vec4 cubicvr_color(vec2 texCoord) { 305 | vec4 color = vec4(0.0,0.0,0.0,0.0); 306 | 307 | #if POINT_SIZE && !POINT_SPRITE && POINT_CIRCLE 308 | if (length(sPos-(gl_FragCoord.xy)) > ptSize/2.0) { 309 | discard; 310 | } 311 | #endif 312 | 313 | #if !LIGHT_DEPTH_PASS 314 | #if TEXTURE_COLOR 315 | #if !(LIGHT_IS_POINT||LIGHT_IS_DIRECTIONAL||LIGHT_IS_SPOT||LIGHT_IS_AREA) 316 | color = texture2D(textureColor, texCoord).rgba; 317 | color.rgb *= materialColor; 318 | //vec4(lightAmbient,1.0)* 319 | #else 320 | color = texture2D(textureColor, texCoord).rgba; 321 | #if !TEXTURE_ALPHA 322 | if (color.a<=0.9) { 323 | discard; 324 | } 325 | #endif 326 | color.rgb *= materialColor; 327 | #endif 328 | #if VERTEX_COLOR 329 | color *= vec4(vertexColorOut,1.0); 330 | #endif 331 | #else 332 | #if VERTEX_COLOR 333 | color = vec4(vertexColorOut,1.0); 334 | #else 335 | color = vec4(materialColor,1.0); 336 | #endif 337 | #endif 338 | 339 | #if TEXTURE_ALPHA 340 | color.a = texture2D(textureAlpha, texCoord).r; 341 | #if FX_DEPTH_ALPHA 342 | if (color.a < 0.9) discard; 343 | #else 344 | #if MATERIAL_ALPHA 345 | color.a *= materialAlpha; 346 | #else 347 | if (color.a < 0.9) discard; 348 | #endif 349 | #endif 350 | #else 351 | #if MATERIAL_ALPHA 352 | color.a = materialAlpha; 353 | #endif 354 | #endif 355 | #endif 356 | 357 | return color; 358 | } 359 | 360 | vec4 cubicvr_lighting(vec4 color_in, vec3 n, vec2 texCoord) { 361 | vec4 color = color_in; 362 | #if !LIGHT_DEPTH_PASS 363 | vec3 accum = lightAmbient; 364 | #if LIGHT_PERPIXEL 365 | #if LIGHT_IS_POINT 366 | 367 | vec3 specTotal = vec3(0.0,0.0,0.0); 368 | 369 | for (int i = 0; i < LIGHT_COUNT; i++) { 370 | 371 | vec3 lightDirection = lightPosition[i]-vertexPositionOut.xyz; 372 | 373 | float dist = length(lightDirection); 374 | 375 | vec3 halfVector = normalize(vec3(0.0,0.0,1.0)+lightDirection); 376 | 377 | float NdotL = max(dot(normalize(lightDirection),n),0.0); 378 | 379 | if (NdotL > 0.0) { 380 | // basic diffuse 381 | float att = clamp(((lightDistance[i]-dist)/lightDistance[i]), 0.0, 1.0)*lightIntensity[i]; 382 | 383 | accum += att * NdotL * lightDiffuse[i] * materialDiffuse; 384 | 385 | float NdotHV = max(dot(n, halfVector),0.0); 386 | 387 | 388 | #if TEXTURE_SPECULAR 389 | vec3 spec2 = lightSpecular[i] * texture2D(textureSpecular, vec2(texCoord.s, texCoord.t)).rgb * pow(NdotHV,materialShininess); 390 | #else 391 | vec3 spec2 = lightSpecular[i] * materialSpecular * pow(NdotHV,materialShininess); 392 | #endif 393 | 394 | specTotal += spec2; 395 | } 396 | 397 | } 398 | 399 | color.rgb *= accum; 400 | color.rgb += specTotal; 401 | #endif 402 | 403 | 404 | 405 | 406 | #if LIGHT_IS_DIRECTIONAL 407 | float NdotL; 408 | float NdotHV = 0.0; 409 | vec3 specTotal = vec3(0.0,0.0,0.0); 410 | vec3 spec2 = vec3(0.0,0.0,0.0); 411 | 412 | vec3 halfVector; 413 | 414 | for (int i = 0; i < LIGHT_COUNT; i++) { 415 | 416 | halfVector = normalize(vec3(0.0,0.0,1.0)-lightDirection[i]); 417 | 418 | NdotL = max(dot(normalize(-lightDirection[i]),n),0.0); 419 | 420 | if (NdotL > 0.0) { 421 | accum += lightIntensity[i] * materialDiffuse * lightDiffuse[i] * NdotL; 422 | 423 | NdotHV = max(dot(n, halfVector),0.0); 424 | 425 | #if TEXTURE_SPECULAR 426 | spec2 = lightSpecular[i] * texture2D(textureSpecular, vec2(texCoord.s, texCoord.t)).rgb * pow(NdotHV,materialShininess); 427 | #else 428 | spec2 = lightSpecular[i] * materialSpecular * pow(NdotHV,materialShininess); 429 | #endif 430 | 431 | specTotal += spec2; 432 | } 433 | } 434 | 435 | color.rgb *= accum; 436 | color.rgb += specTotal; 437 | #endif 438 | 439 | 440 | #if LIGHT_IS_AREA 441 | vec3 specTotal = vec3(0.0,0.0,0.0); 442 | vec3 spec2 = vec3(0.0,0.0,0.0); 443 | float NdotL; 444 | float NdotHV = 0.0; 445 | 446 | vec3 halfVector; 447 | 448 | for (int i = 0; i < LIGHT_COUNT; i++) { 449 | halfVector = normalize(vec3(0.0,0.0,1.0)-lightDirection[i]); 450 | 451 | NdotL = max(dot(normalize(-lightDirection[i]),n),0.0); 452 | 453 | if (NdotL > 0.0) { 454 | 455 | NdotHV = max(dot(n, halfVector),0.0); 456 | 457 | #if LIGHT_SHADOWED 458 | vec4 shadowCoord = lightProjectionOut[i] / lightProjectionOut[i].w; 459 | 460 | shadowCoord.z = DepthRangeA(ConvertDepth3A(shadowCoord.z,lightDepthClip[i].x,lightDepthClip[i].y),lightDepthClip[i].x,lightDepthClip[i].y); 461 | 462 | vec4 shadowSample; 463 | 464 | float shadow = 1.0; 465 | // this seems to get around a shader crash ... 466 | if (shadowCoord.s > 0.000&&shadowCoord.s < 1.000 && shadowCoord.t > 0.000 && shadowCoord.t < 1.000) if (i == 0) { shadow = getShadowVal(lightShadowMap[0],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z);} 467 | #if LIGHT_COUNT>1 468 | if (i == 1) { shadow = getShadowVal(lightShadowMap[1],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 469 | #endif 470 | #if LIGHT_COUNT>2 471 | if (i == 2) { shadow = getShadowVal(lightShadowMap[2],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 472 | #endif 473 | #if LIGHT_COUNT>3 474 | if (i == 3) { shadow = getShadowVal(lightShadowMap[3],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 475 | #endif 476 | #if LIGHT_COUNT>4 477 | if (i == 4) { shadow = getShadowVal(lightShadowMap[4],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 478 | #endif 479 | #if LIGHT_COUNT>5 480 | if (i == 5) { shadow = getShadowVal(lightShadowMap[5],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 481 | #endif 482 | #if LIGHT_COUNT>6 483 | if (i == 6) { shadow = getShadowVal(lightShadowMap[6],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 484 | #endif 485 | #if LIGHT_COUNT>7 486 | if (i == 7) { shadow = getShadowVal(lightShadowMap[7],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 487 | #endif 488 | 489 | accum += shadow * lightIntensity[i] * materialDiffuse * lightDiffuse[i] * NdotL; 490 | #else 491 | accum += lightIntensity[i] * materialDiffuse * lightDiffuse[i] * NdotL; 492 | #endif 493 | 494 | #if TEXTURE_SPECULAR 495 | spec2 = lightSpecular[i] * texture2D(textureSpecular, vec2(texCoord.s, texCoord.t)).rgb * pow(NdotHV,materialShininess); 496 | #else 497 | spec2 = lightSpecular[i] * materialSpecular * pow(NdotHV,materialShininess); 498 | #endif 499 | 500 | #if LIGHT_SHADOWED 501 | spec2 *= shadow; 502 | #endif 503 | 504 | specTotal += spec2; 505 | 506 | #if LIGHT_SHADOWED 507 | // accum = texture2D(lightShadowMap[0], vec2(shadowCoord.s, shadowCoord.t)).rgb; 508 | #endif 509 | 510 | } 511 | } 512 | 513 | color.rgb *= accum; 514 | color.rgb += specTotal; 515 | 516 | #endif 517 | 518 | 519 | #if LIGHT_IS_SPOT 520 | vec3 specTotal = vec3(0.0,0.0,0.0); 521 | vec3 spec2 = vec3(0.0,0.0,0.0); 522 | 523 | vec3 halfVector; 524 | float spotEffect; 525 | float spotDot; 526 | float power; 527 | 528 | for (int i = 0; i < LIGHT_COUNT; i++) { 529 | vec3 l = lightPosition[i]-vertexPositionOut.xyz; 530 | 531 | float dist = length(l); 532 | 533 | float att = clamp(((lightDistance[i]-dist)/lightDistance[i]), 0.0, 1.0)*lightIntensity[i]; 534 | 535 | att = clamp(att,0.0,1.0); 536 | 537 | spotDot = dot(normalize(-l), normalize(lightDirection[i])); 538 | 539 | if ( spotDot < cos((lightCutOffAngle[i]/2.0)*(3.14159/180.0)) ) { 540 | spotEffect = 0.0; 541 | } 542 | else { 543 | spotEffect = pow(spotDot, 1.0); 544 | } 545 | 546 | #if !LIGHT_IS_PROJECTOR 547 | att *= spotEffect; 548 | #endif 549 | 550 | vec3 v = normalize(-vertexPositionOut.xyz); 551 | vec3 h = normalize(l + v); 552 | 553 | float NdotL = max(0.0, dot(n, normalize(l))); 554 | float NdotH = max(0.0, dot(n, h)); 555 | 556 | if (NdotL > 0.0) { 557 | power = pow(NdotH, materialShininess); 558 | } 559 | else { 560 | power = 0.0; 561 | } 562 | 563 | #if LIGHT_SHADOWED 564 | vec4 shadowCoord = lightProjectionOut[i] / lightProjectionOut[i].w; 565 | 566 | shadowCoord.z = DepthRangeA(ConvertDepth3A(shadowCoord.z,lightDepthClip[i].x,lightDepthClip[i].y),lightDepthClip[i].x,lightDepthClip[i].y); 567 | 568 | vec4 shadowSample; 569 | 570 | float shadow = 1.0; 571 | // this seems to get around a shader crash ... 572 | if (shadowCoord.s >= 0.000&&shadowCoord.s <= 1.000 && shadowCoord.t >= 0.000 && shadowCoord.t <= 1.000) if (i == 0) { shadow = getShadowVal(lightShadowMap[0],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z);} 573 | #if LIGHT_COUNT>1 574 | if (i == 1) { shadow = getShadowVal(lightShadowMap[1],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 575 | #endif 576 | #if LIGHT_COUNT>2 577 | if (i == 2) { shadow = getShadowVal(lightShadowMap[2],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 578 | #endif 579 | #if LIGHT_COUNT>3 580 | if (i == 3) { shadow = getShadowVal(lightShadowMap[3],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 581 | #endif 582 | #if LIGHT_COUNT>4 583 | if (i == 4) { shadow = getShadowVal(lightShadowMap[4],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 584 | #endif 585 | #if LIGHT_COUNT>5 586 | if (i == 5) { shadow = getShadowVal(lightShadowMap[5],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 587 | #endif 588 | #if LIGHT_COUNT>6 589 | if (i == 6) { shadow = getShadowVal(lightShadowMap[6],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 590 | #endif 591 | #if LIGHT_COUNT>7 592 | if (i == 7) { shadow = getShadowVal(lightShadowMap[7],shadowCoord,lightProjectionOut[i].w,lightDepthClip[i].z); } 593 | #endif 594 | 595 | att = att * shadow; 596 | #endif 597 | 598 | #if LIGHT_IS_PROJECTOR && LIGHT_SHADOWED 599 | if (shadowCoord.s >= 0.0&&shadowCoord.s <= 1.0 && shadowCoord.t >= 0.0 && shadowCoord.t <= 1.0 && spotDot > cos((90.0)*(3.14159/180.0))) { 600 | vec3 projTex = texture2D(lightProjectionMap[i],shadowCoord.st).rgb; 601 | accum += att * projTex * lightIntensity[i] * materialDiffuse * lightDiffuse[i] * NdotL; 602 | } 603 | #else 604 | accum += att * lightDiffuse[i] * materialDiffuse * NdotL; 605 | #endif 606 | 607 | #if TEXTURE_SPECULAR 608 | spec2 = lightSpecular[i] * texture2D(textureSpecular, vec2(texCoord.s, texCoord.t)).rgb * power; 609 | #else 610 | spec2 = lightSpecular[i] * materialSpecular * power; 611 | #endif 612 | 613 | #if LIGHT_SHADOWED 614 | spec2 *= shadow; 615 | #endif 616 | 617 | specTotal += spec2*spotEffect; 618 | 619 | } 620 | 621 | 622 | color.rgb *= accum; 623 | color.rgb += specTotal; 624 | 625 | #if LIGHT_SHADOWED 626 | // color = texture2D(lightShadowMap[0], vec2(texCoord.s, texCoord.t)).rgba; 627 | 628 | #endif 629 | #endif 630 | #else 631 | // vertex lighting 632 | #if LIGHT_IS_POINT||LIGHT_IS_DIRECTIONAL||LIGHT_IS_SPOT||LIGHT_IS_AREA 633 | color.rgb *= lightColorOut; 634 | color.rgb += lightSpecularOut; 635 | #endif 636 | #endif // LIGHT_PERPIXEL 637 | 638 | #if TEXTURE_AMBIENT 639 | #if LIGHT_IS_POINT||LIGHT_IS_DIRECTIONAL||LIGHT_IS_SPOT||LIGHT_IS_AREA 640 | color.rgb += texture2D(textureAmbient, texCoord).rgb*(vec3(1.0,1.0,1.0)+materialColor*materialAmbient); 641 | #else 642 | color.rgb = color.rgb*texture2D(textureAmbient, texCoord).rgb; 643 | #endif 644 | #else 645 | #if TEXTURE_COLOR 646 | color.rgb += materialAmbient*texture2D(textureColor, texCoord).rgb; 647 | #else 648 | color.rgb += materialColor*materialAmbient; 649 | #endif 650 | #endif 651 | #endif 652 | 653 | #if FOG_ENABLED 654 | return apply_fog(color); 655 | #else 656 | return color; 657 | #endif 658 | } 659 | 660 | vec4 cubicvr_environment(vec4 color_in, vec3 n, vec2 texCoord) { 661 | vec4 color = color_in; 662 | #if !LIGHT_DEPTH_PASS 663 | #if TEXTURE_REFLECT 664 | float environmentAmount = texture2D( textureReflect, texCoord).r; 665 | #endif 666 | 667 | #if TEXTURE_ENVSPHERE 668 | #if TEXTURE_NORMAL 669 | vec3 r = reflect( envTexCoordOut, n ); 670 | float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); 671 | 672 | vec3 coord; 673 | coord.s = r.x/m + 0.5; 674 | coord.t = r.y/m + 0.5; 675 | 676 | #if TEXTURE_REFLECT 677 | color.rgb += materialColor*texture2D( textureEnvSphere, coord.st).rgb * environmentAmount; 678 | #else 679 | color.rgb += materialColor*texture2D( textureEnvSphere, coord.st).rgb * materialEnvironment; 680 | #endif 681 | #else 682 | #if TEXTURE_REFLECT 683 | color.rgb += materialColor*texture2D( textureEnvSphere, envTexCoordOut).rgb * environmentAmount; 684 | #else 685 | color.rgb += materialColor*texture2D( textureEnvSphere, envTexCoordOut).rgb * materialEnvironment; 686 | #endif 687 | #endif 688 | #endif // TEXTURE_ENVSPHERE 689 | 690 | #endif // ! LIGHT_DEPTH_PASS 691 | 692 | #if FX_DEPTH_ALPHA 693 | #if !MATERIAL_ALPHA 694 | float linear_depth = DepthRange( ConvertDepth3(gl_FragCoord.z) ); 695 | 696 | color.a = linear_depth; 697 | #endif 698 | #endif 699 | return color; 700 | } 701 | 702 | 703 | #if LIGHT_DEPTH_PASS 704 | vec4 cubicvr_depthPack(vec2 texCoord) { 705 | #if TEXTURE_ALPHA 706 | float alphaVal = texture2D(textureAlpha, texCoord).r; 707 | if (alphaVal < 0.9) discard; 708 | #endif 709 | 710 | return packFloatToVec4i(DepthRange( ConvertDepth3(gl_FragCoord.z))); 711 | } 712 | #endif 713 | 714 | #define customShader_splice 1 715 | 716 | void main(void) 717 | { 718 | vec2 texCoord = cubicvr_texCoord(); 719 | 720 | #if !LIGHT_DEPTH_PASS 721 | vec4 color = cubicvr_color(texCoord); 722 | vec3 normal = cubicvr_normal(texCoord); 723 | 724 | color = cubicvr_environment(color,normal,texCoord); 725 | color = cubicvr_lighting(color,normal,texCoord); 726 | 727 | gl_FragColor = clamp(color,0.0,1.0); 728 | #else // LIGHT_DEPTH_PASS for shadows, discard to cut 729 | gl_FragColor = cubicvr_depthPack(texCoord); 730 | #endif 731 | } 732 | -------------------------------------------------------------------------------- /src/CubicVR_Core.vs: -------------------------------------------------------------------------------- 1 | attribute vec3 vertexPosition; 2 | attribute vec3 vertexNormal; 3 | attribute vec2 vertexTexCoord; 4 | 5 | #if VERTEX_COLOR 6 | attribute vec3 vertexColor; 7 | varying vec3 vertexColorOut; 8 | #endif 9 | 10 | #if VERTEX_MORPH 11 | attribute vec3 vertexMorphPosition; 12 | attribute vec3 vertexMorphNormal; 13 | uniform float materialMorphWeight; 14 | #endif 15 | 16 | #if POINT_SIZE||POINT_SPRITE 17 | uniform float pointSize; 18 | #endif 19 | 20 | #if POINT_SIZE && !POINT_SPRITE && POINT_CIRCLE 21 | varying float ptSize; 22 | #if POINT_CIRCLE 23 | varying vec2 sPos; 24 | uniform vec3 viewPort; 25 | #endif 26 | #endif 27 | 28 | varying vec2 vertexTexCoordOut; 29 | uniform vec2 materialTexOffset; 30 | 31 | #if !LIGHT_PERPIXEL 32 | #if LIGHT_IS_POINT||LIGHT_IS_DIRECTIONAL||LIGHT_IS_SPOT||LIGHT_IS_AREA 33 | uniform vec3 lightDirection[LIGHT_COUNT]; 34 | uniform vec3 lightPosition[LIGHT_COUNT]; 35 | uniform vec3 lightSpecular[LIGHT_COUNT]; 36 | uniform vec3 lightDiffuse[LIGHT_COUNT]; 37 | uniform float lightIntensity[LIGHT_COUNT]; 38 | uniform float lightDistance[LIGHT_COUNT]; 39 | #if LIGHT_IS_SPOT 40 | uniform float lightCutOffAngle[LIGHT_COUNT]; 41 | #endif 42 | 43 | varying vec3 lightColorOut; 44 | varying vec3 lightSpecularOut; 45 | #endif 46 | 47 | uniform vec3 materialDiffuse; 48 | uniform vec3 materialSpecular; 49 | uniform float materialShininess; 50 | 51 | #endif 52 | 53 | 54 | // #if TEXTURE_COLOR||TEXTURE_BUMP||TEXTURE_NORMAL||TEXTURE_AMBIENT||hasSpecularMap||hasAlphaMap 55 | // #endif 56 | 57 | uniform mat4 matrixModelView; 58 | uniform mat4 matrixProjection; 59 | uniform mat4 matrixObject; 60 | uniform mat3 matrixNormal; 61 | 62 | varying vec3 vertexNormalOut; 63 | varying vec4 vertexPositionOut; 64 | 65 | #if !LIGHT_DEPTH_PASS 66 | 67 | 68 | #if LIGHT_SHADOWED 69 | varying vec4 lightProjectionOut[LIGHT_COUNT]; 70 | uniform mat4 lightShadowMatrix[LIGHT_COUNT]; 71 | #endif 72 | 73 | 74 | #if TEXTURE_ENVSPHERE 75 | #if TEXTURE_NORMAL 76 | varying vec3 envTexCoordOut; 77 | #else 78 | varying vec2 envTexCoordOut; 79 | #endif 80 | #endif 81 | 82 | 83 | 84 | #if TEXTURE_BUMP||TEXTURE_NORMAL 85 | varying vec3 envEyeVectorOut; 86 | #endif 87 | 88 | #endif // !LIGHT_DEPTH_PASS 89 | 90 | 91 | 92 | void cubicvr_normalMap() { 93 | #if !LIGHT_DEPTH_PASS 94 | #if TEXTURE_BUMP||TEXTURE_NORMAL 95 | vec3 tangent; 96 | vec3 binormal; 97 | 98 | vec3 c1 = cross( vertexNormal, vec3(0.0, 0.0, 1.0) ); 99 | vec3 c2 = cross( vertexNormal, vec3(0.0, 1.0, 0.0) ); 100 | 101 | if ( length(c1) > length(c2) ) { 102 | tangent = c1; 103 | } else { 104 | tangent = c2; 105 | } 106 | 107 | tangent = normalize(tangent); 108 | 109 | binormal = cross(vertexNormal, tangent); 110 | binormal = normalize(binormal); 111 | 112 | mat4 uMVOMatrix = matrixModelView * matrixObject; 113 | 114 | mat3 TBNMatrix = mat3( (vec3 (uMVOMatrix * vec4 (tangent, 0.0))), 115 | (vec3 (uMVOMatrix * vec4 (binormal, 0.0))), 116 | (vec3 (uMVOMatrix * vec4 (vertexNormal, 0.0))) 117 | ); 118 | 119 | envEyeVectorOut = vec3(uMVOMatrix * vec4(vertexPosition,1.0)) * TBNMatrix; 120 | #endif 121 | #endif 122 | } 123 | 124 | void cubicvr_environmentMap() { 125 | #if !LIGHT_DEPTH_PASS 126 | #if TEXTURE_ENVSPHERE 127 | #if TEXTURE_NORMAL 128 | envTexCoordOut = normalize( vertexPositionOut.xyz ); 129 | #else 130 | vec3 ws = (matrixModelView * vec4(vertexPosition,1.0)).xyz; 131 | vec3 r = reflect(ws, vertexNormalOut ); 132 | float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); 133 | envTexCoordOut.s = r.x/m + 0.5; 134 | envTexCoordOut.t = r.y/m + 0.5; 135 | #endif 136 | #endif 137 | #if VERTEX_COLOR 138 | vertexColorOut = vertexColor; 139 | #endif 140 | #endif 141 | } 142 | 143 | void cubicvr_shadowMap() { 144 | #if (LIGHT_IS_SPOT||LIGHT_IS_AREA) && LIGHT_SHADOWED 145 | for (int i = 0; i < LIGHT_COUNT; i++) 146 | { 147 | #if LIGHT_SHADOWED 148 | #if VERTEX_MORPH 149 | lightProjectionOut[i] = lightShadowMatrix[i] * (matrixObject * vec4(vertexPosition+(vertexMorphPosition-vertexPosition)*materialMorphWeight, 1.0)); 150 | #else 151 | lightProjectionOut[i] = lightShadowMatrix[i] * (matrixObject * vec4(vertexPosition, 1.0)); 152 | #endif 153 | #endif 154 | } 155 | #endif 156 | } 157 | 158 | void cubicvr_lighting() { 159 | #if !LIGHT_PERPIXEL 160 | #if LIGHT_IS_POINT 161 | 162 | vec3 specTotal = vec3(0.0,0.0,0.0); 163 | vec3 accum = vec3(0.0,0.0,0.0); 164 | 165 | for (int i = 0; i < LIGHT_COUNT; i++) { 166 | 167 | vec3 lightDirection = lightPosition[i]-vertexPositionOut.xyz; 168 | 169 | float dist = length(lightDirection); 170 | 171 | vec3 halfVector = normalize(vec3(0.0,0.0,1.0)+lightDirection); 172 | 173 | float NdotL = max(dot(normalize(lightDirection),vertexNormalOut),0.0); 174 | 175 | if (NdotL > 0.0) { 176 | // basic diffuse 177 | float att = clamp(((lightDistance[i]-dist)/lightDistance[i]), 0.0, 1.0)*lightIntensity[i]; 178 | 179 | accum += att * NdotL * lightDiffuse[i] * materialDiffuse; 180 | 181 | float NdotHV = max(dot(vertexNormalOut, halfVector),0.0); 182 | 183 | vec3 spec2 = lightSpecular[i] * materialSpecular * pow(NdotHV,materialShininess); 184 | 185 | specTotal += spec2; 186 | } 187 | 188 | } 189 | 190 | lightColorOut = accum; 191 | lightSpecularOut = specTotal; 192 | #endif 193 | 194 | #if LIGHT_IS_DIRECTIONAL 195 | float NdotL; 196 | float NdotHV = 0.0; 197 | vec3 specTotal = vec3(0.0,0.0,0.0); 198 | vec3 spec2 = vec3(0.0,0.0,0.0); 199 | vec3 accum = vec3(0.0,0.0,0.0); 200 | 201 | vec3 halfVector; 202 | 203 | for (int i = 0; i < LIGHT_COUNT; i++) { 204 | 205 | halfVector = normalize(vec3(0.0,0.0,1.0)-lightDirection[i]); 206 | 207 | NdotL = max(dot(normalize(-lightDirection[i]),vertexNormalOut),0.0); 208 | 209 | if (NdotL > 0.0) { 210 | accum += lightIntensity[i] * materialDiffuse * lightDiffuse[i] * NdotL; 211 | 212 | NdotHV = max(dot(vertexNormalOut, halfVector),0.0); 213 | 214 | spec2 = lightSpecular[i] * materialSpecular * pow(NdotHV,materialShininess); 215 | 216 | specTotal += spec2; 217 | } 218 | } 219 | 220 | lightColorOut = accum; 221 | lightSpecularOut = specTotal; 222 | #endif 223 | 224 | #if LIGHT_IS_SPOT 225 | vec3 specTotal = vec3(0.0,0.0,0.0); 226 | vec3 spec2 = vec3(0.0,0.0,0.0); 227 | vec3 accum = vec3(0.0,0.0,0.0); 228 | 229 | vec3 halfVector; 230 | float spotEffect; 231 | float spotDot; 232 | float power; 233 | 234 | for (int i = 0; i < LIGHT_COUNT; i++) { 235 | vec3 l = lightPosition[i]-vertexPositionOut.xyz; 236 | 237 | float dist = length(l); 238 | 239 | float att = clamp(((lightDistance[i]-dist)/lightDistance[i]), 0.0, 1.0)*lightIntensity[i]; 240 | 241 | att = clamp(att,0.0,1.0); 242 | 243 | spotDot = dot(normalize(-l), normalize(lightDirection[i])); 244 | 245 | if ( spotDot < cos((lightCutOffAngle[i]/2.0)*(3.14159/180.0)) ) { 246 | spotEffect = 0.0; 247 | } 248 | else { 249 | spotEffect = pow(spotDot, 1.0); 250 | } 251 | 252 | att *= spotEffect; 253 | 254 | vec3 v = normalize(-vertexPositionOut.xyz); 255 | vec3 h = normalize(l + v); 256 | 257 | float NdotL = max(0.0, dot(vertexNormalOut, normalize(l))); 258 | float NdotH = max(0.0, dot(vertexNormalOut, h)); 259 | 260 | if (NdotL > 0.0) { 261 | power = pow(NdotH, materialShininess); 262 | } 263 | else { 264 | power = 0.0; 265 | } 266 | 267 | 268 | accum += att * lightDiffuse[i] * materialDiffuse * NdotL; 269 | 270 | spec2 = lightSpecular[i] * materialSpecular * power; 271 | 272 | specTotal += spec2*spotEffect; 273 | 274 | } 275 | 276 | lightColorOut = accum; 277 | lightSpecularOut = specTotal; 278 | #endif 279 | #endif // !LIGHT_PERPIXEL 280 | cubicvr_normalMap(); 281 | cubicvr_shadowMap(); 282 | cubicvr_environmentMap(); 283 | } 284 | 285 | 286 | 287 | vec2 cubicvr_texCoord() { 288 | return vertexTexCoord + materialTexOffset; 289 | } 290 | 291 | 292 | vec4 cubicvr_transform() { 293 | #if LIGHT_DEPTH_PASS 294 | vertexNormalOut = vec3(0.0,0.0,0.0); 295 | #endif 296 | 297 | #if VERTEX_MORPH 298 | vec4 vPos = matrixObject * vec4(vertexPosition+(vertexMorphPosition-vertexPosition)*materialMorphWeight, 1.0); 299 | #else 300 | vec4 vPos = matrixObject * vec4(vertexPosition, 1.0); 301 | #endif 302 | 303 | vertexPositionOut = matrixModelView * vPos; 304 | 305 | #if POINT_SIZE||POINT_SPRITE 306 | float d = length(vertexPositionOut); 307 | gl_PointSize = pointSize * sqrt( 1.0/(1.0 + d*d) ); 308 | 309 | #if !POINT_SPRITE && POINT_CIRCLE 310 | ptSize = gl_PointSize; 311 | vec4 screenPos = vec4(matrixProjection * vertexPositionOut); 312 | sPos = (screenPos.xy/screenPos.w)*vec2(viewPort.x/2.0,viewPort.y/2.0)+vec2(viewPort.x/2.0+0.5,viewPort.y/2.0+0.5); 313 | #endif 314 | #endif 315 | 316 | return vPos; 317 | } 318 | 319 | vec3 cubicvr_normal() { 320 | #if VERTEX_MORPH 321 | return normalize(matrixObject*vec4(vertexNormal+(vertexMorphNormal-vertexNormal)*materialMorphWeight,0.0)).xyz; 322 | #else 323 | return normalize(matrixObject*vec4(vertexNormal,0.0)).xyz; 324 | #endif 325 | } 326 | 327 | #define customShader_splice 1 328 | 329 | void main(void) 330 | { 331 | vertexTexCoordOut = cubicvr_texCoord(); 332 | gl_Position = matrixProjection * matrixModelView * cubicvr_transform(); 333 | 334 | #if !LIGHT_DEPTH_PASS // not needed if shadowing 335 | 336 | vertexNormalOut = matrixNormal * cubicvr_normal(); 337 | 338 | cubicvr_lighting(); 339 | 340 | #endif // !LIGHT_DEPTH_PASS 341 | } 342 | -------------------------------------------------------------------------------- /src/cl/fracture.cl: -------------------------------------------------------------------------------- 1 | struct Tri { 2 | float4 a, b, c; // x y z _ 3 | }; 4 | 5 | /// Row-major matrix multiplication, assuming `v` is a point (w=1) 6 | inline float4 matmul(float16 m, float4 v) { 7 | v.w = 1.0; 8 | float4 mv = { 9 | dot(m.s0123, v), 10 | dot(m.s4567, v), 11 | dot(m.s89AB, v), 12 | dot(m.sCDEF, v) 13 | }; 14 | return mv; 15 | } 16 | 17 | kernel void transformCopyPerPlane( 18 | /*0*/ uint planecount, 19 | /*1*/ float16 matrix, // The transformation matrix 20 | /*2*/ uint tricount, // Original number of triangles 21 | /*3*/ global int *tricells, // Does not have to be initialized, but should be size planecount*tricount 22 | /*4*/ global struct Tri *tris // Needs to be initialized only for the first tricount elems, but same 23 | ) { 24 | uint i_tri = get_global_id(0); 25 | if (i_tri >= tricount) { 26 | return; 27 | } 28 | 29 | struct Tri t = tris[i_tri]; 30 | t.a = matmul(matrix, t.a); 31 | t.b = matmul(matrix, t.b); 32 | t.c = matmul(matrix, t.c); 33 | 34 | for (int i_plane = 0; i_plane < planecount; i_plane++) { 35 | tricells[i_plane * tricount + i_tri] = i_plane; 36 | tris [i_plane * tricount + i_tri] = t; 37 | } 38 | } 39 | 40 | kernel void applyProximity( 41 | /*0*/ constant uint *prox, // true/false proximate per cell 42 | /*1*/ uint tricount, 43 | /*2*/ global int *tricells // Modified in place 44 | ) { 45 | uint i_tri = get_global_id(0); 46 | if (i_tri >= tricount) { 47 | return; 48 | } 49 | 50 | int c = tricells[i_tri]; 51 | if (c != -1 && !prox[c]) { 52 | tricells[i_tri] = -2; // -1 means delete, -2 doesn't! 53 | } 54 | } 55 | 56 | kernel void getScanInput( 57 | /*0*/ uint count, 58 | /*1*/ constant int *cellnums, // List of cell indices pertaining to another array. If -1, then corresponding index of that array will be removed. 59 | /*2*/ uint outcount, // this needs to be a power of 2. 60 | /*3*/ global int *scaninput // starts as an array of length (minimum power of 2 greater than count). 0 if cellnum[index] is -1, 1 otherwise. 61 | ){ 62 | uint index = get_global_id(0); 63 | if (index >= outcount) { 64 | return; 65 | } 66 | if (index == 0) { 67 | scaninput[index] = 0; 68 | return; 69 | } 70 | uint readindex = index - 1; 71 | if (index < count) { 72 | if (cellnums[readindex] > -1) { 73 | scaninput[index] = 1; 74 | } else { 75 | scaninput[index] = 0; 76 | } 77 | } else { 78 | scaninput[index] = 0; 79 | } 80 | } 81 | 82 | // note that you could potentially do this with a single array that stores both out and in interleaved, and alternates by doing iter%2. 83 | kernel void scanIter( 84 | /*0*/ uint iter, // iteration counter. starts at 1. 85 | /*1*/ uint count, 86 | /*2*/ global int *bufA, // Scan array used as input this iteration. 87 | /*3*/ global int *bufB // Scan array used as output this iteration. 88 | ){ 89 | uint index = get_global_id(0); 90 | if (index >= count) { 91 | return; 92 | } 93 | int pow2 = 1; 94 | for (int i = 0; i < iter; i++) { 95 | pow2 *= 2; 96 | } 97 | if (index >= pow2) { 98 | if (iter % 2 == 0) { 99 | bufB[index] = bufA[index - pow2] + bufA[index]; 100 | } else { 101 | bufA[index] = bufB[index - pow2] + bufB[index]; 102 | 103 | } 104 | } else { 105 | if (iter % 2 == 0) { 106 | bufB[index] = bufA[index]; 107 | } else { 108 | bufA[index] = bufB[index]; 109 | } 110 | } 111 | } 112 | 113 | // may need separate scatters (eg. scatterTri, scatterPoints, ...) 114 | // don't need scanInput's output because you can just check if tricells[index] == -1 115 | kernel void scatterTris( 116 | /*0*/ uint tricount, // size of original array 117 | /*1*/ constant struct Tri *tris, // len is tricount 118 | /*2*/ constant int *tricells, // len is tricount 119 | /*3*/ constant int *scatterout, // len is (minimum power of 2 greater than tricount) 120 | /*4*/ uint trioutcount, // size of new array 121 | /*5*/ global struct Tri *triout, // len is scatterout[tricount] 122 | /*6*/ global int *trioutcells // len is scatterout[tricount] 123 | ){ 124 | uint index = get_global_id(0); 125 | if (index >= tricount) { 126 | return; 127 | } 128 | 129 | if (tricells[index] != -1 && scatterout[index] < trioutcount) { 130 | trioutcells[scatterout[index]] = tricells[index]; 131 | triout[scatterout[index]] = tris[index]; 132 | } 133 | } 134 | 135 | // don't need scanInput's output because you can just check if tricells[index] == -1 136 | kernel void scatterPoints( 137 | /*0*/ uint pointcount, // size of original array 138 | /*1*/ constant float4 *points, // len is pointcount 139 | /*2*/ constant int *pointcells, // len is pointcount 140 | /*3*/ constant int *scatterout, // len is (minimum power of 2 greater than pointcount) 141 | /*4*/ uint pointoutcount, // size of new array 142 | /*5*/ global float4 *pointout, // len is scatterout[pointcount] 143 | /*6*/ global int *pointoutcells // len is scatterout[pointcount] 144 | ){ 145 | uint index = get_global_id(0); 146 | if (index >= pointcount) { 147 | return; 148 | } 149 | 150 | if (pointcells[index] != -1 && scatterout[index] < pointoutcount) { 151 | pointoutcells[scatterout[index]] = pointcells[index]; 152 | pointout[scatterout[index]] = points[index]; 153 | } 154 | } 155 | 156 | kernel void fracture( 157 | // Fracture pattern planes: index these by tricells 158 | /*0*/ uint planecount, 159 | /*1*/ constant float4 *planes, // Nx Ny Nz d 160 | 161 | // Input mesh triangles (initially, one copy per cell) 162 | /*2*/ uint tricount, 163 | /*3*/ constant int *tricells, // len is tricount 164 | /*4*/ constant struct Tri *tris, // len is tricount 165 | 166 | // Output mesh triangles 167 | /*5*/ global int *trioutcells, // len is tricount*2, -1 for nonexistent 168 | /*6*/ global struct Tri *triout, // len is tricount*2 169 | 170 | // Output new vertices for new face 171 | /*7*/ global int *newoutcells, // len is tricount, -1 for nonexistent 172 | /*8*/ global float4 *newout, // len is tricount*2 173 | 174 | /*9*/ float4 fracCenter 175 | ) { 176 | uint index = get_global_id(0); 177 | if (index >= tricount) { 178 | return; 179 | } 180 | 181 | int cell = tricells[index]; 182 | float4 _pla = planes[cell]; 183 | if (_pla.x == 0 && _pla.y == 0 && _pla.z == 0) { 184 | // this cell doesn't have a plane on this iteration; do nothing 185 | trioutcells[2 * index] = cell; 186 | triout[2 * index] = tris[index]; 187 | trioutcells[2 * index + 1] = -1; 188 | newoutcells[index] = -1; 189 | return; 190 | } 191 | float4 pN = {_pla.xyz, 0}; 192 | // move the plane into local coordinate system. 193 | float pd = _pla.w + dot(pN, fracCenter); 194 | float4 pP = {0, 0, -pd / pN.z, 0}; // Arbitrarily calculate a point on the plane (z-axis intersection) 195 | 196 | struct Tri tri = tris[index]; 197 | 198 | // TODO: perform plane-triangle clip 199 | bool cull1, cull2, cull3, tempb; 200 | bool winding = true; // keeps track of whether or not the winding is still consistent. 201 | cull1 = dot(pN, tri.a - pP) < 0; 202 | cull2 = dot(pN, tri.b - pP) < 0; 203 | cull3 = dot(pN, tri.c - pP) < 0; 204 | 205 | float4 p1 = tri.a; 206 | float4 p2 = tri.b; 207 | float4 p3 = tri.c; 208 | // sort the points from culled to not culled. 209 | if (!cull1) { // if cull1 is false, swap 1 and 3 (order 321) 210 | // is this faster than putting if-else? if (cull3){...} else if (cull2){...} 211 | cull1 = cull3; 212 | cull3 = false; 213 | p1 = tri.c; 214 | p2 = tri.b; 215 | p3 = tri.a; 216 | 217 | winding = false; 218 | 219 | if (!cull1) { // if it's still false, swap 1 and 2 (final order 231) 220 | cull1 = cull2; 221 | cull2 = false; 222 | p1 = tri.b; 223 | p2 = tri.c; 224 | 225 | winding = true; 226 | } 227 | } else if (!cull2) { 228 | cull2 = cull3; 229 | cull3 = false; 230 | 231 | p1 = tri.a; 232 | p2 = tri.c; 233 | p3 = tri.b; 234 | 235 | winding = false; 236 | } 237 | 238 | // note that it's configured to output only the original triangle by default. 239 | struct Tri newTri1, newTri2; 240 | newTri1 = tri; // current triangle. 241 | int cell1 = cell; // cell of the current face. 242 | int cell2 = -1; // cell of the new face. 243 | int cellP = -1; // cell of the new points (for newoutcells). 244 | float4 newP1, newP2; // new points. 245 | 246 | if (cull3) { // if all 3 points are culled, do nothing. Output is -1 247 | // set cell1 to -1. 248 | cell1 = -1; 249 | } else if (!cull1) { // if all 3 points are not culled, add to output normally. 250 | // do nothing. 251 | } else if (!cull2) { // XOR: if only one point is culled (p1), needs new face, add both to output 252 | // calculate new edge p1-p2 253 | float4 v = normalize(p1 - p2); 254 | newP1 = p2 + v * -(dot(p2, pN) + pd) / dot(v, pN); 255 | 256 | // calculate new edge p1-p3 257 | v = normalize(p1 - p3); 258 | newP2 = p3 + v * -(dot(p3, pN) + pd) / dot(v, pN); 259 | 260 | newTri1.a = newTri2.a = p2; 261 | if (winding) { 262 | newTri1.b = newP2; 263 | newTri1.c = newP1; 264 | newTri2.b = p3; 265 | newTri2.c = newP2; 266 | } else { 267 | newTri1.b = newP1; 268 | newTri1.c = newP2; 269 | newTri2.b = newP2; 270 | newTri2.c = p3; 271 | } 272 | 273 | // Both new faces and new points 274 | cell2 = cellP = cell; 275 | } else { // two points culled (p1, p2), modify current face and add to output 276 | // calculate new edge p2 - p3 277 | float4 v = normalize(p2 - p3); 278 | newP1 = p3 + v * -(dot(p3, pN) + pd) / dot(v, pN); 279 | 280 | // calculate new edge p1-p3 281 | v = normalize(p1 - p3); 282 | newP2 = p3 + v * -(dot(p3, pN) + pd) / dot(v, pN); 283 | 284 | // set new points 285 | newTri1.a = newP1; 286 | if (winding) { 287 | newTri1.b = p3; 288 | newTri1.c = newP2; 289 | } else { 290 | newTri1.b = newP2; 291 | newTri1.c = p3; 292 | } 293 | 294 | // just new points. 295 | cellP = cell; 296 | } 297 | 298 | // output triangles. 299 | trioutcells[2 * index] = cell1; 300 | triout[2 * index] = newTri1; 301 | trioutcells[2 * index + 1] = cell2; 302 | triout[2 * index + 1] = newTri2; 303 | 304 | // output new points (for later triangulation). 305 | newoutcells[index] = cellP; 306 | if (winding) { 307 | newout[2 * index] = newP1; 308 | newout[2 * index + 1] = newP2; 309 | } else { 310 | newout[2 * index] = newP2; 311 | newout[2 * index + 1] = newP1; 312 | } 313 | 314 | /* 315 | // the following should do nothing. 316 | trioutcells[2 * index] = cell; 317 | triout[2 * index] = tris[index]; 318 | trioutcells[2 * index + 1] = -1; 319 | newoutcells[index] = -1; 320 | */ 321 | } 322 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |4 0 5 0 1 0 5 1 6 1 2 1 6 2 7 2 3 2 7 3 4 3 0 3 0 4 1 4 2 4 7 5 6 5 5 5 0 6 4 6 1 6 1 7 5 7 2 7 2 8 6 8 3 8 3 9 7 9 0 9 3 10 0 10 2 10 4 11 7 11 5 11
82 |0 0 8 0 9 0 1 1 9 1 10 1 2 2 10 2 11 2 3 3 11 3 12 3 12 4 13 4 5 4 5 5 13 5 14 5 14 6 15 6 7 6 0 7 7 7 15 7 16 8 17 8 9 8 9 9 17 9 18 9 18 10 19 10 11 10 19 11 20 11 12 11 12 12 20 12 21 12 13 13 21 13 22 13 14 14 22 14 23 14 15 15 23 15 16 15 16 16 24 16 25 16 25 17 26 17 18 17 18 18 26 18 27 18 19 19 27 19 28 19 28 20 29 20 21 20 29 21 30 21 22 21 22 22 30 22 31 22 23 23 31 23 24 23 32 24 33 24 25 24 33 25 34 25 26 25 34 26 35 26 27 26 35 27 36 27 28 27 28 28 36 28 37 28 29 29 37 29 38 29 30 30 38 30 39 30 31 31 39 31 32 31 32 32 40 32 41 32 33 33 41 33 42 33 42 34 43 34 35 34 35 35 43 35 44 35 44 36 45 36 37 36 37 37 45 37 46 37 46 38 47 38 39 38 47 39 40 39 32 39 40 40 48 40 49 40 49 41 50 41 42 41 50 42 51 42 43 42 43 43 51 43 52 43 52 44 53 44 45 44 45 45 53 45 54 45 46 46 54 46 55 46 55 47 48 47 40 47 48 48 56 48 57 48 49 49 57 49 58 49 50 50 58 50 59 50 51 51 59 51 60 51 60 52 61 52 53 52 61 53 62 53 54 53 62 54 63 54 55 54 63 55 56 55 48 55 56 56 64 56 65 56 65 57 66 57 58 57 58 58 66 58 67 58 59 59 67 59 68 59 60 60 68 60 69 60 69 61 70 61 62 61 62 62 70 62 71 62 63 63 71 63 64 63 72 64 73 64 65 64 73 65 74 65 66 65 74 66 75 66 67 66 67 67 75 67 76 67 68 68 76 68 77 68 69 69 77 69 78 69 70 70 78 70 79 70 71 71 79 71 72 71 72 72 80 72 81 72 73 73 81 73 82 73 74 74 82 74 83 74 83 75 84 75 76 75 76 76 84 76 85 76 85 77 86 77 78 77 86 78 87 78 79 78 87 79 80 79 72 79 88 80 89 80 81 80 81 81 89 81 90 81 82 82 90 82 91 82 83 83 91 83 92 83 92 84 93 84 85 84 93 85 94 85 86 85 94 86 95 86 87 86 87 87 95 87 88 87 96 88 97 88 89 88 89 89 97 89 98 89 90 90 98 90 99 90 99 91 100 91 92 91 92 92 100 92 101 92 101 93 102 93 94 93 102 94 103 94 95 94 95 95 103 95 96 95 96 96 0 96 1 96 1 97 2 97 98 97 98 98 2 98 3 98 99 99 3 99 4 99 4 100 5 100 101 100 5 101 6 101 102 101 102 102 6 102 7 102 7 103 0 103 96 103 1 104 0 104 9 104 2 105 1 105 10 105 3 106 2 106 11 106 4 107 3 107 12 107 4 108 12 108 5 108 6 109 5 109 14 109 6 110 14 110 7 110 8 111 0 111 15 111 8 112 16 112 9 112 10 113 9 113 18 113 10 114 18 114 11 114 11 115 19 115 12 115 13 116 12 116 21 116 14 117 13 117 22 117 15 118 14 118 23 118 8 119 15 119 16 119 17 120 16 120 25 120 17 121 25 121 18 121 19 122 18 122 27 122 20 123 19 123 28 123 20 124 28 124 21 124 21 125 29 125 22 125 23 126 22 126 31 126 16 127 23 127 24 127 24 128 32 128 25 128 25 129 33 129 26 129 26 130 34 130 27 130 27 131 35 131 28 131 29 132 28 132 37 132 30 133 29 133 38 133 31 134 30 134 39 134 24 135 31 135 32 135 33 136 32 136 41 136 34 137 33 137 42 137 34 138 42 138 35 138 36 139 35 139 44 139 36 140 44 140 37 140 38 141 37 141 46 141 38 142 46 142 39 142 39 143 47 143 32 143 41 144 40 144 49 144 41 145 49 145 42 145 42 146 50 146 43 146 44 147 43 147 52 147 44 148 52 148 45 148 46 149 45 149 54 149 47 150 46 150 55 150 47 151 55 151 40 151 49 152 48 152 57 152 50 153 49 153 58 153 51 154 50 154 59 154 52 155 51 155 60 155 52 156 60 156 53 156 53 157 61 157 54 157 54 158 62 158 55 158 55 159 63 159 48 159 57 160 56 160 65 160 57 161 65 161 58 161 59 162 58 162 67 162 60 163 59 163 68 163 61 164 60 164 69 164 61 165 69 165 62 165 63 166 62 166 71 166 56 167 63 167 64 167 64 168 72 168 65 168 65 169 73 169 66 169 66 170 74 170 67 170 68 171 67 171 76 171 69 172 68 172 77 172 70 173 69 173 78 173 71 174 70 174 79 174 64 175 71 175 72 175 73 176 72 176 81 176 74 177 73 177 82 177 75 178 74 178 83 178 75 179 83 179 76 179 77 180 76 180 85 180 77 181 85 181 78 181 78 182 86 182 79 182 79 183 87 183 72 183 80 184 88 184 81 184 82 185 81 185 90 185 83 186 82 186 91 186 84 187 83 187 92 187 84 188 92 188 85 188 85 189 93 189 86 189 86 190 94 190 87 190 80 191 87 191 88 191 88 192 96 192 89 192 90 193 89 193 98 193 91 194 90 194 99 194 91 195 99 195 92 195 93 196 92 196 101 196 93 197 101 197 94 197 94 198 102 198 95 198 88 199 95 199 96 199 97 200 96 200 1 200 97 201 1 201 98 201 99 202 98 202 3 202 100 203 99 203 4 203 100 204 4 204 101 204 101 205 5 205 102 205 103 206 102 206 7 206 103 207 7 207 96 207
76 |0 0 1 0 2 0 1 1 0 1 5 1 0 2 2 2 3 2 0 3 3 3 4 3 0 4 4 4 5 4 1 5 5 5 10 5 2 6 1 6 6 6 3 7 2 7 7 7 4 8 3 8 8 8 5 9 4 9 9 9 1 10 10 10 6 10 2 11 6 11 7 11 3 12 7 12 8 12 4 13 8 13 9 13 5 14 9 14 10 14 6 15 10 15 11 15 7 16 6 16 11 16 8 17 7 17 11 17 9 18 8 18 11 18 10 19 9 19 11 19
82 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
76 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
109 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
142 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
175 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
208 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
241 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
274 |3 0 2 0 0 0 0 1 2 1 1 1 0 2 1 2 3 2
307 |1 0 2 0 0 0
76 |1 0 2 0 0 0
109 |2 0 1 1 3 2 0 3 1 1 2 0 3 2 1 1 0 3 0 3 2 0 3 2
76 |2 0 1 1 3 2 0 3 1 1 2 0 3 2 1 1 0 3 0 3 2 0 3 2
76 |4 0 5 0 1 0 5 1 6 1 2 1 6 2 7 2 3 2 7 3 4 3 0 3 0 4 1 4 2 4 7 5 6 5 5 5 0 6 4 6 1 6 1 7 5 7 2 7 2 8 6 8 3 8 3 9 7 9 0 9 3 10 0 10 2 10 4 11 7 11 5 11
76 |4 0 5 0 1 0 5 1 6 1 2 1 6 2 7 2 3 2 7 3 4 3 0 3 0 4 1 4 2 4 7 5 6 5 5 5 0 6 4 6 1 6 1 7 5 7 2 7 2 8 6 8 3 8 3 9 7 9 0 9 3 10 0 10 2 10 4 11 7 11 5 11
76 |