# Refraction light pattern

**URL:** <https://discourse.processing.org/t/refraction-light-pattern/8731>\
**Category:** Processing.py\
**Created:** [February 26, 2019, 9:29am UTC](https://discourse.processing.org/t/refraction-light-pattern/8731 "2019-02-26T09:29:31Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![netasoreq](https://avatars.discourse-cdn.com/v4/letter/n/8797f3/32.png) [@netasoreq](https://discourse.processing.org/u/netasoreq)\
**Post date:** [February 26, 2019, 9:29am UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/1 "2019-02-26T09:29:31Z")

</div>

Hi ! i would like to do code in processing, that look like refraction light pattern .  
Someone have any suggest how to start ?  
 ![download](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/3/3fc2ca7840589bc0cf28289a8d094e7193dbab2e.jpeg)

---

<div class="post-metadata">

**Author:** ![hotfooted](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/hotfooted/32/3670_2.png) [@hotfooted](https://discourse.processing.org/u/hotfooted)\
**Post date:** [February 26, 2019, 9:42am UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/2 "2019-02-26T09:42:35Z")

</div>

if you want to fake it there’s a good guide [here](https://gamedev.stackexchange.com/a/150389/122932) it’s not processing but you could port it over.

---

<div class="post-metadata">

**Author:** ![solub](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/solub/32/333_2.png) [@solub](https://discourse.processing.org/u/solub)\
**Post date:** [February 27, 2019, 12:50am UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/3 "2019-02-27T00:50:06Z")

</div>

Hi @netasoreq,

I can’t help but to see Voronoi pattern in your picture.

Do you want your final output to be a static image ? If so, pixel displacement based on [Worley noise](https://en.wikipedia.org/wiki/Worley_noise) (or even [ridged noise](https://www.google.com/search?q=ridged+noise&client=firefox-b-d&source=lnms&tbm=isch&sa=X&ved=0ahUKEwihw-CE2NrgAhVN3RoKHTyvDKsQ_AUIDigB&biw=1472&bih=737)) could be a solution. See [this thread](https://discourse.processing.org/t/2d-displacement-mapping-for-water-effect/3191/4) for more info on noise-based pixel displacement.

---

<div class="post-metadata">

**Author:** ![netasoreq](https://avatars.discourse-cdn.com/v4/letter/n/8797f3/32.png) [@netasoreq](https://discourse.processing.org/u/netasoreq)\
**Post date:** [February 27, 2019, 7:43am UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/4 "2019-02-27T07:43:27Z")

</div>

Hi @solub !  
thank you very much, your link is very cool !!  
may you send me the full code of the [2D displacement mapping for water effect](https://discourse.processing.org/t/2d-displacement-mapping-for-water-effect/3191/6)  
it’s could be very helpful to learn from this…

TNX again !!!  
Neta

---

<div class="post-metadata">

**Author:** ![solub](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/solub/32/333_2.png) [@solub](https://discourse.processing.org/u/solub)\
**Post date:** [February 27, 2019, 1:52pm UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/5 "2019-02-27T13:52:48Z")

</div>

> [@netasoreq](#):
>
> may you send me the full code

It IS the full code but because it is written in Python you need to port it to Java.

Note that this example sketch is implementing Fractal Brownian Noise.

 ![Fractal%20Brownian%20Noise](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/d/d4004cbfc6d2f297ec5a4387664c3b6552dcc4e3.jpeg)

```auto
def setup():
    size(400, 300, P2D)
        
    loadPixels()
    for y in range(height):
        for x in range(width):
            i = x + y * width
            n = warp(x, y, .01, 255)
            pixels[i] = color(-n, n, n)
    updatePixels()
            
def warp(_x, _y, factor, n_range):
    n1 = noise((_x+0.0) * factor, (_y+0.0) * factor) * n_range
    n2 = noise((_x+5.2) * factor, (_y+1.3) * factor) * n_range
    q = PVector(n1, n2)
            
    n3 = noise(((_x + q.x * 4) + 1.7) * factor, ((_y + q.y * 4) + 9.2) * factor) * n_range
    n4 = noise(((_x + q.x * 4) + 8.3) * factor, ((_y + q.y * 4) + 2.8) * factor) * n_range
    r = PVector(n3, n4)
                
    return noise((_x + r.x * 4) * factor, (_y + r.y * 4) * factor) * n_range

```

But for what you’re trying to achieve I was suggesting Ridged Noise instead …

 ![RidgedNoise](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/e/e891f059b0a5bf3de5406225e2c8e3bddadcedbd.png)

EDIT: just raised ridged noise to the power of 4 and decreased `NoiseDetail()` to 3 for better results

```auto
def setup():
    size(400, 300, P2D)
        
    noiseDetail(3)
    loadPixels()
    for y in range(height):
        for x in range(width):
            i = x + y * width
            n = ridgedNoise(x, y, .007, 80, 120)
            pixels[i] = color(0, n, n)
    updatePixels()
            
def ridgedNoise(_x, _y, factor, minimum, maximum):
    n = noise(_x * factor, _y * factor)
    return minimum + pow((2 * (.5 - abs(.5 - n))), 4) * maximum

```

… or even Worley Noise (similar to Voronoi Noise)

 ![Worley%20Noise](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/7/72ef09a0077c984d6531e3240fcdcc7c3a5abc1f.jpeg)

```auto
def setup():
    size(400, 300, P2D)
    
    points = [PVector(random(width), random(height)) for i in range(40)]
    maxDist = 0.0
    
    loadPixels()
    for y in range(height):
        for x in range(width):
            dists = [dist(x, y, p.x, p.y) for p in points]
            minDist = min(dists)
            if minDist > maxDist: maxDist = minDist
            c = map(pow((minDist / maxDist), 2), 0, 1, 55, 255)
            i = x + y * width
            pixels[i] = color(10, 40 + c, 40 + c)
    updatePixels()

```

Now, for a more realistic output you can try to:

- fiddle with the `noiseDetail()` function (for the first 2 examples)
- mix different kind of noises / algorithm
- …

If you want to make an animation then I would suggest:

- to port the algorithm of your choice to GLSL and increment the noise value as the skecth is iterating
- to use [Spatial Hashing](https://www.google.com/search?client=firefox-b-d&ei=mWVjXKSJBM-6a96tv-AK&q=spatial+hash&oq=spatial+hash&gs_l=psy-ab.3..35i39l2j0i20i263j0l5j0i67j0.2616.2941..3312...0.0..0.105.264.2j1......0....1..gws-wiz.......0i71j0i22i30.UQtYJXZXE14) for efficient distances computation (for Worley Noise)
- or to implement the Navier-Stokes equations for fluid simulation (preferably in a shader). Daniel Shiffman did it in Processing Java in a [recent coding challenge](https://www.youtube.com/watch?v=alhpH6ECFvQ).

---

<div class="post-metadata">

**Author:** ![netasoreq](https://avatars.discourse-cdn.com/v4/letter/n/8797f3/32.png) [@netasoreq](https://discourse.processing.org/u/netasoreq)\
**Post date:** [February 28, 2019, 11:04am UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/6 "2019-02-28T11:04:21Z")

</div>

super cool thank you very much !!! you relay help me , many tnx !

---

<div class="post-metadata">

**Author:** ![netasoreq](https://avatars.discourse-cdn.com/v4/letter/n/8797f3/32.png) [@netasoreq](https://discourse.processing.org/u/netasoreq)\
**Post date:** [March 5, 2019, 2:09pm UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/7 "2019-03-05T14:09:35Z")

</div>

Hi @solub …  
may I ask you question ?  
maybe you some idea why it’s not wrap the width stripe of the pic’s?

 ![1](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/8/8247ac0d67e1344f876628c23c3482d3365ba3cb.jpeg)

---

<div class="post-metadata">

**Author:** ![solub](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/solub/32/333_2.png) [@solub](https://discourse.processing.org/u/solub)\
**Post date:** [March 5, 2019, 3:03pm UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/8 "2019-03-05T15:03:23Z")

</div>

I’m sorry, I’m unable to reproduce your output.

Maybe try to replace the following lines

```auto
n = warp(x, y, .003, 1) #Be sure to keep n_range (last value) at 1
c = img.pixels[int(round(i * n))] 
pixels[i] = c

```

Edit: Just tried with a random image with black and white stripes, it should work:

 ![WartStripes](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/3/380d8b75baa351bcbaec762e128fb43341ab740f.png)

---

<div class="post-metadata">

**Author:** ![solub](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/solub/32/333_2.png) [@solub](https://discourse.processing.org/u/solub)\
**Post date:** [March 25, 2020, 8:32pm UTC](https://discourse.processing.org/t/refraction-light-pattern/8731/9 "2020-03-25T20:32:21Z")

</div>

I have recently stumbled upon this [video tutorial](https://www.youtube.com/watch?v=GPYlgxMaMQs) on “caustics” in Houdini that reminded me of this question. I realize now that it was what the OP was actually looking for, so I am posting a more appropriate answer for future readers.

As explained in the video linked above caustics are obtained by simulating light refraction from one medium (air for example) to another (water in this case). The algorithm consists in the following steps:

- Shoot vertical rays of light `PVector(0,0,-1)` on a water surface (yellow noised `grid`)
- Compute refracted rays (red) using [Snell’s law](https://en.wikipedia.org/wiki/Snell%27s_law)
- Find the intersection points (blue) of these rays with the plane underneath

Caustics become apparent where the point-density is high (rays pointing in the same direction)

![ezgif.com-optimize(2)](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/4/431dc2b4aa2033f29f02c2db0d36a86c6a86ea38.gif)

Computing refraction for thousands of rays in 3D being quite expensive a workaround could consist in lowering the grid resolution and applying a blur filter on the projected points.

Here below and example with a 128\*72 grid:

 ![Caustics (2)](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/a/a27e09838cf0961e1cd97a0fa0328e83ba4e3009.jpeg)

```auto
add_library('hemesh')

W, H = 128, 72 #dimension of grid ex: 1024/576
nFaces = W*H #number of Faces
nVerts = (W+1)*(H+1) #number of Vertices
F = .018 #Factor of noise
Z = 100 #Z-coordinate of plane

dir = WB_Vector(0, 0, -1) #direction of light
pln = WB_Plane(WB_Point(-100, -100, -Z), WB_Vector(0, -1, -Z)) #plane(origin, direction)

def setup():
    size(1280, 720, P3D)
    translate(width>>1, height>>1)
    background('#003638')
    noiseSeed(2)
    
    # compute noise value for each vertex
    nvalues = [noise(i%(W+1) * F, i//(W+1) * F) * 100 for i in xrange(nVerts)]
    
    # create grid with chosen dimensions and feed it with height values (noise values)
    grid = HEC_Grid().setU(W).setV(H).setUSize(width).setVSize(height).setWValues(nvalues)
    
    # construct mesh
    mesh = HE_Mesh(grid) 
    
    # computes refracted rays + render intersection points
    for i in xrange(nFaces):
        
        n = mesh.getFaceNormal(i)
        c = mesh.getFaceCenter(i)
        
        ray = refract(dir, n, c, 1.0, 1.33) # 1.0 / 1.33 = refraction indices of air and water
        sec = WB_GeometryOp.getIntersection3D(ray, pln) # find if ray intersects the plane
        ipt = sec.object # intersection point
        
        d = ipt.getDistance(c.sub(WB_Point(0, 0, Z))) # how far is itp from its face's normal
        
        cl = map(d, .7, 57, 255, 36) # map distance to color value        
        sw = map(d, .7, 57, 6, 0) # map distance to stroke weight
        
        strokeWeight(sw)
        stroke(0, cl, cl)
        point(ipt.xf(), ipt.yf())
      
    filter(DILATE)  
    filter(BLUR, 6)  

    noLoop()
    
    
def refract(k, n, o, i1, i2):
    
    # Based on -> https://github.com/pmocherla/OOP-RayTracer-/blob/master/raytracer.py
    
    """
    Returns a WB_Vector.
    Computes the 3D direction vector of a refracted ray through a surface using Snell's law.
        
    Args:
        k (WB_Vector) - incident ray direction vector
        n (WB_Vector) - vector normal to refracting surface
        o (WB_Point) - origin point of refracted ray / intersection point of the incident ray with surface
        i1 (float) - refractive index of media 1
        i2 (float) - refractive index of media 2
    """
    
    #Calculate the angle between the ray and the surface normal
    theta = acos(k.dot(n) / n.getSqLength())
    
    #Condition for total internal reflection must not be satisfied.
    if sin(theta) < i2/i1:
        
        k.normalizeSelf()
        n.normalizeSelf()
        cprod = n.cross(k)
        ior = i1/i2 #index of reflection

        #Calculation of Snells law broken into two parts and combined
        one = n.cross(cprod).mul(ior)
        two = n.mul(sqrt(1 - ior**2 * cprod.dot(cprod)))
        dir = one.sub(two)
        
        return WB_Ray(o, dir)

```
