Skip to content

lcrs/_.hips

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aligning arrows to follow a pyro sim... don't mind that it's on a shoe (loud video):

Ls_AF1TransonicRegime_v01_8mb.mp4

Unwrapping layers of papyrus from CT scans of the Herculaneum scrolls - could see the crosshatching of the reeds woven together but no ink or writing (loud video):

Ls_AncientScrolls_v01.mp4

APEX node graphs are stored as geometry so you can do silly things like move the nodes themselves around with attribnoise:

Ls_ApexLol_v01.mp4

Using the vague concept of a "volumetric tangent space" for blendshaping so it doesn't move linearly, but follows the surface of the sphere to get to its final point (left is normal linear blendshape with obvious intersection and volume problems):

Screen_Recording_2024-07-08_at_21.37.49.mp4

Mantra rendered caustics in a quite different way to how Karma now does and they can still be fun to play with... just set photon target on a caustic light to the transparent object and add a zero to the default photon count, then constantly hit render in the IPR to update the map:

Ls_Caustics_v01

Semi-serious try at 3D colour correction with 5 from/to pickers using radial basis functions to stay smooth, kinda similar to the mesh-like colour warping in Baselight/Flame/Resolve:

Ls_ColourBooper_v01.mp4

Using OpenCL global atomic add tricks to render a buffer of point positions as single pixel particles super fast, a bit like Krakatoa or higx point render:

Ls_AtomicAdd2c.mp4
#bind layer src read
#bind layer dummy write opt
#bind layer dst write

// https://violetspace.github.io/blog/atomic-float-addition-in-opencl.html
inline void atomic_add_f(volatile __global float* addr, const float val) {
    #if defined(cl_nv_pragma_unroll)
            float ret; asm volatile("atom.global.add.f32 %0,[%1],%2;":"=f"(ret):"l"(addr),"f"(val):"memory");
    #elif defined(__opencl_c_ext_fp32_global_atomic_add)
            atomic_fetch_add_explicit((volatile global atomic_float*)addr, val, memory_order_relaxed);
    #elif __has_builtin(__builtin_amdgcn_global_atomic_fadd_f32)
            __builtin_amdgcn_global_atomic_fadd_f32(addr, val);
    #else
            float old = val; while((old=atomic_xchg(addr, atomic_xchg(addr, 0.0f)+old))!=0.0f);
    #endif
}

@KERNEL {
    int2 xy = convert_int2(@src.xy * @dst.res);
    int idx = _linearIndex(@dst.stat, xy);
    atomic_add_f((global float *)_bound_dst + idx, 0.01);  
}

Crude demo of 6 million particles simulating and rendering at 60fps all on the GPU in COPs, using a simulate block with position and velocity buffers and the above atomic add trick:

Ls_Cop3GPUparticles_v01.smal.mp4

Similar to above but also cheaply rasterizing the particles onto an image layer inside the simulate block then using the derivatives of that image to get a force towards the local concentration of particles, like hard-to-control but very fast flocking or the particle/grid transfer that forms the basis of FLIP:

Ls_Cop3GPUparticles_v03.smol2.mp4

There's no way to apply a 3D LUT in new-style COPs apart from the OCIO node (which is CPU-only) but if you encode one in an image you can do a reasonable job with OpenCL:

Ls_Cop3LUTfromImage_v01

Binding a 3@matrix attribute into OpenCL using float9, then loading it with mat3load(). Admittedly it's confusing you can't access the mat3 as an array of 9 floats since it's actually an array of fpreal3s (each of which is 4 floats wide even more confusingly):

copmatrix
#bind layer src? val=0
#bind layer !&dst
#bind point mat float9 port=geo

@KERNEL
{
    // Load 3@ matrix attrib from point chosen by pixel y coord
    mat3 m;
    mat3load(0, @mat.tupleAt(@iy), m);
    @dst.set((float4)(m[0].x, m[0].y, m[0].z, m[1].x));
}

The Prefix Sum COP makes summed area tables that let you do box filters of any size pretty much instantly, so you can add up tons of them to approximate the non-separable kernels of exponential or fibonacci glows (which look uncomfortably like having smudged glasses if you get the shape just right):

Ls_Cop3PrefixsumGlow
#bind layer src
#bind layer psum
#bind layer !&dst
#bind parm size float
#bind parm shape float
#bind parm layers int

@KERNEL {
    float w = @size / 20;
    float4 acc = 0.0;
    for(int i = 0; i < @layers; i++) {
        // https://en.wikipedia.org/wiki/Summed-area_table
        acc += (@psum(@P + (float2)(w, w)) + @psum(@P + (float2)(-w, -w)) - @psum(@P + (float2)(-w, w)) - @psum(@P + (float2)(w, -w)))
               / (float4)(2 * w * @res.x * w * @res.y);
        w *= 1 + @shape;
    }

    @dst.set(@src + acc / (float4)@layers);
}

The Smooth Fill COP can be used for seamless edge blending, useful for tiled textures:

Ls_Cop3Tile_v01

Camera projection in COPs by simply projecting UVs from camera as a uv2 attrib in SOPs, then baking from uv2 to uv:

Ls_Cop3UVProject

Extrapolating a curve using Taylor polynomials (can enable gradient3 and gradient4 as well but it gets a bit spicy):

Screen_Recording_2024-07-15_at_17.17.56.mp4

Velocity extrapolation weirdness in DOPs... when velocity sampling is set to streak, velocity at the border is reflected back into the system. Changing velocity sampling to corner keeps it stable, as does turning closed boundaries on (but changes the look a lot). Bizarrely it also stays stable if you enable OpenCL on Gas Enforce Boundary DOP:

Screen_Recording_2025-08-10_at_13.48.02.mp4

Easily memorable way to transfer a sculpt or other shape change from a rest pose to a different pose by storing the difference in tangent space, simply using dot products with N/tangentu/tangentv without needing to remember which bit is which in a TBN matrix (may not match the MikkT standard though):

Ls_GeoTangentSpaceTransfer_v02

Splits a mesh into low and high frequency parts, then uses the tangent space transfer trick above to offset the high frequencies in UV space:

Ls_GeoFreqSep_v02_8mb.mp4

Demo of strangely warped reflections at close-to-tangent ray incidence - goes away in Karma XPU or when changing vertex order/normal direction:

Ls_GlancingReflectionBug_v01

Generates a list of OpenCL functions in Houdini's include folder using the cpp preprocessor and https://ctags.io/ - output looks like Ls_houCLfuncs.cl

The HDK comes with a Karma procedural that renders a mandelbulb in VEX without having to create any geometry (sadly it crashes as soon as you change any of the parameters in 20.5.361 unless you switch the renderer from Karma CPU to something else and then back again):

Ls_KarmaMandelbulb

Probably the best we can do in 21.0 to render PNGs from Karma with an OCIO view transform baked in, by applying it with a slapcomp block and passing --ocio 0 to husk so it doesn't do another transform... it matches rendering an EXR and applying the view transform afterwards in COPs:

Ls_KarmaPNGslapcompACES_v01

You can sorta render a sunset using a uniform volume on a sphere as big as the entire earth with a geo-referenced DEM terrain, letting the atmosphere both scatter and absorb the same colour so the sky gradient from the low-angle sun appears naturally:

Ls_KarmaSunset_v01

Pruning primitives outside of an animated camera's accumulated viewing volume by sampling a SOPs VDB at the center of each prim in LOPs to check if it's outside:

Ls_LOPsFrustrumClipMaybe_v01
vector center = usd_getbbox_center(0, @primpath, @primpurpose);
float sdf = volumesample('op:/stage/sdf/frustrum/FrustrumVDB', 0, center);
if(sdf > 0.0) {
    usd_setactive(0, @primpath, 0);
}

Extracts a best-fit 3x3 matrix from two Macbeth chart images similarly to mmColorTarget using everyone's favourite, the Linear Solver SOP:

Ls_MatrixFromChart_v01

Interpolating between two matrices using slerp() so the transform stays rigid and doesn't introduce weird shears like interpolating the matrix values directly does:

Ls_MatrixInterp_v01

Soap bubble simulated as 2D smoke with a noise texture mapped to the advected "single rest" field in COPs, rendered with Arnold's thin film shader - doing that keeps far more detail than trying to advect the noise texture directly, and resizing the rest field up 3x before mapping makes it look as if the whole thing was done with an extra 3x3 worth of antialiasing:

Ls_OldBubbleSim_v07_8mb.mp4

Keeps only packed prims that are visible from a point by casting rays - it may seem surprising that the Ray SOP can trace against packed prims since most SOPs only treat them as a single point, but thinking of them as render time instances hints that it should work and be fast... the rays record hitprim from the packed geo, then findattribvalcount() checks if each piece had a hit recorded:

Ls_PackedGeoRayCull

Silly experiment advecting the control points of beziers with a pyro sim:

Ls_PyroType_v03.mp4

Simplifying colour ramps that have way too many points by treating them as 3D paths in RGB space:

Ls_RampRefine.mp4

Visualizing the main result of https://arxiv.org/abs/2502.14367v3 by showing rotation vectors in SO(3) for 50 walks as their angle scales increase over time, both on the left walking once (which never returns to the origin) and on the right the same walk repeated twice (which returns to the origin many times):

Ls_RotationWalksHome_v03.mp4

2D ink simulation from scratch using only microsolvers, blending back the effect of divergence-free projection to avoid excessive mushroom shapes:

Ls_Semicompressible_Ink2d_v01.mp4

Faking shadows using geometry raycast away from a point light source.

Ls_Shadowgeo_v01

VEX snippet to squash some primitives and accumulate the offset to maintain the spacing of the others.

Ls_SquashSomePrims_v01
int squashcount = 0;
for(int i = 0; i < nprimitives(0); i++) {
    if(inprimgroup(0, "squash", i-1)) {
        squashcount++;
    }
    foreach(int p; primpoints(0, i)) {
        vector oldpos = point(0, 'P', p);
        vector newpos = oldpos;
        newpos.x -= squashcount * chf('squashfactor');
        setpointattrib(0, 'P', p, newpos);
    }
}

Cheap trick to make meshes squish away from each other when in contact without any simulation:

Ls_Squish_v01.mp4

Generates a 2D straight skeleton by advecting points toward the SDF center, similar to Labs Straight Skeleton 2D:

Ls_Skeletonize_v01

Creates a smooth terrain from contour lines using thin plate spline interpolation, adapted from the RBF blendshape example in the Linear Solver SOP help page:

Ls_TerrainFromContours_v01

Extracts the Q-criterion isosurface beloved by aerospace CFD people from a smoke or pyro sim (it's the difference between the squared frobenius norms of the symmetric and anti-symmetric parts of the velocity gradient tensor, see https://www.m4-engineering.com/q-criterion-for-vortex-visualization):

Ls_TurbulenceQ_v01.mp4

Builds a geometry network with friend/following relations from a social media graph and simulates it to see which parts cluster naturally together (big mess, for more see https://github.com/lcrs/twarea):

Ls_TwitterArea_v04_8mb.mp4

Showing how deceptive the viewport can be when working with fast-moving cameras - the capy and camera are in sync when hitting play, but when scrubbing the timeline he's all over the shop:

capyyyy.mp4

Using tag visualizers in dummy geo objects parented under the camera. This allows color control, and is easy to move around rather than being stuck in the corner.

Ls_ViewportText_v01

   

Everything else in here I have zero memory of don't @ me 🤍 lewis.saunders@gmail.com

(Older stuff might be on https://lewisinthelandofmachines.tumblr.com)

About

.hips: do they lie? our experts investigate (various houdini tools in here)

Topics

Resources

Stars

Watchers

Forks

Contributors 2

  •  
  •