Results 1 to 10 of 10

Thread: Epsilon selection for bump generation from alpha?

  1. #1
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143

    Epsilon selection for bump generation from alpha?

    My Simplex Noise node is nearly finished, but I'd like to add an output that generates a bump vector from multi-sampling so I can include effects from the Function input, along with any crazy things the user inputs in the alpha channels. (The regular, faster, bump vector output uses the direct-gradient calculation made by the simplex noise generator, but that can't incorporate the Function input and nor possibly the contrast setting.)

    According to this article (section 5.6) I can do this using only three extra samples by getting the values from three surrounding points from the main spot coordinate by adding a small epsilon value.

    What I can't figure out is what value to use. Is there some commonly-accepted constant I should use? Do I need to generate it somehow?

  2. #2
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    Does no one know?

    Curses.

    I'll have to start experimenting, then.

  3. #3
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    (if only the newtek node devs read this forum... they must have a good answer to this. )

  4. #4
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    Finally, the music for that darned commercial is done! Now I can get back to node coding...

    I haven't run this yet, but this is what I'm going to be trying for epsilon generation and noise multi-sampling:

    Code:
            // -------------------------------------------------------------------- Neighborhod_Epsilon()
            inline double Neighborhod_Epsilon( LWTypes::LWNodalAccess * nodal_access )
            {
                const double epsilon = (nodal_access->spotSize * 0.5) * nodal_access->cosine;
                
                return (epsilon > 0.0) ? epsilon : std::numeric_limits<double>::min();
            }
    Since the spot size gets more and more inaccurate at glancing angles (the "spot" will actually be a long, skinny area), I'm attempting to pull in the epsilon offset so it's not overly-large. The more glancing the angle, the more the offset will be reigned back in.


    After that, I just sample the three offset locations and I'm hoping that later today I'll have this all wired into the code that will calculate a gradient based on these three values + the original spot's noise value (calculated elsewhere in the code):

    Code:
        // ---------------------------------------------------------------------------- Calculate_Simplex_Neighborhood()
        // Samples noise at three nearby coordinates from the original spot position
        
        Simplex_Noise::Simplex_Neighborhood_t
        
        Simplex_Noise::Calculate_Simplex_Neighborhood( LWTypes::LWDVector       spot_position,
                                                       Gradient_Flags_t         calculate_gradients,
                                                       LWTypes::LWNodalAccess * nodal_access )
        {
            // Determine offset of nearby coordinates
            const double epsilon = Neighborhod_Epsilon( nodal_access );
            
            // Generate the three neighborhood coordinates
            const LWTypes::LWDVector Neighbor_X( spot_position.X() + epsilon, spot_position.Y(),           spot_position.Z()           );
            const LWTypes::LWDVector Neighbor_Y( spot_position.X(),           spot_position.Y() + epsilon, spot_position.Z()           );
            const LWTypes::LWDVector Neighbor_Z( spot_position.X(),           spot_position.Y(),           spot_position.Z() + epsilon );
            
            // Sample noise at the neighborhood coordinates
            return Simplex_Neighborhood_t( epsilon,
                                           Calculate_Simplex( Neighbor_X, Gradient_Flags_t::No_Gradients, nodal_access ).Noise,
                                           Calculate_Simplex( Neighbor_Y, Gradient_Flags_t::No_Gradients, nodal_access ).Noise,
                                           Calculate_Simplex( Neighbor_Z, Gradient_Flags_t::No_Gradients, nodal_access ).Noise );
        }

    Thanks to C++11's 'move' semantics, returning the Simplex_Neighborhood object by value shouldn't incur any extra value-copying overhead:

    Code:
        // ============================================================================
        // Simplex Neighborhood
        //
        // Holds three sampled noise values -- used for bump generation when the
        //                                     directly-calculated gradient won't do.
        // ----------------------------------------------------------------------------
        template < typename value_t >
        
        class Simplex_Neighborhood
        {
        private:
            // -------------------------------------------------------------------- Values
            // coordinate epsilon, (x, y, z, w)
            const std::array< value_t, 5 > noise_EXYZW;
            
        public:
            // -------------------------------------------------------------------- Accessors
            double Epsilon() const { return noise_EXYZW[0]; }
            double Noise_X() const { return noise_EXYZW[1]; }
            double Noise_Y() const { return noise_EXYZW[2]; }
            double Noise_Z() const { return noise_EXYZW[3]; }
            double Noise_W() const { return noise_EXYZW[4]; }
            
            
        public:
            // -------------------------------------------------------------------- Constructor ( constant )
            Simplex_Neighborhood() : noise_EXYZW( {0.0, 0.0, 0.0, 0.0, 0.0} ) {}
            
            // -------------------------------------------------------------------- Constructor ( copy/move )
            Simplex_Neighborhood( const Simplex_Neighborhood &  source ) : noise_EXYZW(           source.noise_EXYZW  ) {}
            Simplex_Neighborhood(       Simplex_Neighborhood && source ) : noise_EXYZW( std::move(source.noise_EXYZW) ) {}
            
            // -------------------------------------------------------------------- Constructor ( from values )
            Simplex_Neighborhood( value_t epsilon,
                                  value_t x,
                                  value_t y = 0.0,
                                  value_t z = 0.0,
                                  value_t w = 0.0 )
            
                : noise_EXYZW( {epsilon, x, y, z, w} ) {}
        };
    Last edited by jrandom; 05-12-2013 at 11:06 AM.

  5. #5
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    (This is, of course, assuming that the spot size get erroneously large at glancing angles. I do not yet know if this is actually the case.)

    Edit: Some experimenting in Layout tells me I should ignore nodal_access->cosine for the time being and just use the spot size directly...
    Last edited by jrandom; 05-12-2013 at 03:43 PM.

  6. #6
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    It works!

    For my epsilon offset value, I'm just using the spot size's radius (nodal_access->spotSize * 0.5).

    Click image for larger version. 

Name:	MultiSample_Bump.png 
Views:	221 
Size:	79.3 KB 
ID:	114265

    "Fast Bump" uses the simplex noise's directly-generated bump gradient. "Noise Bump" multi-samples the noise and generates the bump gradient from those, allowing Contrast and Function to influence the bump map in the way you'd intuitively expect:

    Click image for larger version. 

Name:	MultiSample_BumpFunction.png 
Views:	219 
Size:	53.4 KB 
ID:	114266

  7. #7
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    Time to multi-sample the alpha forground/background inputs!

    I... will be shocked if this works on the first attempt.

    Code:
        // ---------------------------------------------------------------------------- Get_Alpha_Neighborhood()
        LWTypes::LWDVector Simplex_Noise::Get_Alpha_Neighborhood ( LWTypes::LWNodalAccess * nodal_access )
        {
            // NOTE: Is only accessed by Alpha_Bump, so is not cached. Calculate directly.
            
            // Determine offset of nearby coordinates
            const double epsilon = Neighborhod_Epsilon( nodal_access );
            
            
            // Generate the three neighborhood coordinates
            const LWTypes::LWDVector object_spot( nodal_access->oPos );
            const LWTypes::LWDVector world_spot ( nodal_access->wPos );
            
            LWTypes::LWDVector object_neighbor_X( object_spot.X() + epsilon, object_spot.Y(),           object_spot.Z()           );
            LWTypes::LWDVector object_neighbor_Y( object_spot.X(),           object_spot.Y() + epsilon, object_spot.Z()           );
            LWTypes::LWDVector object_neighbor_Z( object_spot.X(),           object_spot.Y(),           object_spot.Z() + epsilon );
            
            LWTypes::LWDVector world_neighbor_X ( world_spot.X()  + epsilon, world_spot.Y(),            world_spot.Z()            );
            LWTypes::LWDVector world_neighbor_Y ( world_spot.X(),            world_spot.Y()  + epsilon, world_spot.Z()            );
            LWTypes::LWDVector world_neighbor_Z ( world_spot.X(),            world_spot.Y(),            world_spot.Z()  + epsilon );
            
            
            // Create nodal_access copies with offset coordinates
            auto Copy_Nodal_Access = [&]( LWTypes::LWDVector object_neighbor,
                                          LWTypes::LWDVector world_neighbor ) -> LWTypes::LWNodalAccess
            {
                LWTypes::LWNodalAccess nodal_access_copy = *nodal_access;
                
                // Assign offset coordintes and return
                memcpy( nodal_access_copy.oPos, (double*)object_neighbor, sizeof(double) * 3 );
                memcpy( nodal_access_copy.wPos, (double*)world_neighbor,  sizeof(double) * 3 );
                
                return nodal_access_copy;
            };
            
            LWTypes::LWNodalAccess nodal_access_X = Copy_Nodal_Access( object_neighbor_X, world_neighbor_X );
            LWTypes::LWNodalAccess nodal_access_Y = Copy_Nodal_Access( object_neighbor_Y, world_neighbor_Y );
            LWTypes::LWNodalAccess nodal_access_Z = Copy_Nodal_Access( object_neighbor_Z, world_neighbor_Z );
            
            
            // Sample alpha foreground and background at offset locations
            const double alpha_fg_neighbor_X = Input_Alpha_Foreground.Get( &nodal_access_X );
            const double alpha_fg_neighbor_Y = Input_Alpha_Foreground.Get( &nodal_access_Y );
            const double alpha_fg_neighbor_Z = Input_Alpha_Foreground.Get( &nodal_access_Z );
            
            const double alpha_bg_neighbor_X = Input_Alpha_Background.Get( &nodal_access_X );
            const double alpha_bg_neighbor_Y = Input_Alpha_Background.Get( &nodal_access_Y );
            const double alpha_bg_neighbor_Z = Input_Alpha_Background.Get( &nodal_access_Z );
            
            
            // Get noise neighborhood for mixing amounts
            const Simplex_Neighborhood_t noise_neighborhood( Get_Noise_Neighborhood(nodal_access) );
    
            
            // Mix alpha amounts and return
            return LWTypes::LWDVector ( LERP( noise_neighborhood.Noise_X(), alpha_bg_neighbor_X, alpha_fg_neighbor_X ),
                                        LERP( noise_neighborhood.Noise_Y(), alpha_bg_neighbor_Y, alpha_fg_neighbor_Y ),
                                        LERP( noise_neighborhood.Noise_Z(), alpha_bg_neighbor_Z, alpha_fg_neighbor_Z ) );
        }
    Last edited by jrandom; 05-12-2013 at 09:51 PM.

  8. #8
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    Worked the first time out of the gate. Huh. Go figure.

    Click image for larger version. 

Name:	Bump_from_Alpha.jpg 
Views:	219 
Size:	408.4 KB 
ID:	114269

    Click image for larger version. 

Name:	Bump_from_Alpha_Nodes.png 
Views:	216 
Size:	141.6 KB 
ID:	114270

  9. #9
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    And to wrap it all up:

    It occurred to me that since I have separate fg/bg input bump vectors that get mixed into all of the bump outputs, I could do a side-by-side comparison of the difference between a regular bump output from a LW node, and the alpha-generated bump map from that same node.

    The tinted areas are bump-from-alpha, and the grey areas are the bump map straight from the incoming node:

    Click image for larger version. 

Name:	Mixed_Bump.jpg 
Views:	219 
Size:	522.5 KB 
ID:	114271

    Click image for larger version. 

Name:	Mixed_Bump_Nodes.png 
Views:	208 
Size:	121.2 KB 
ID:	114272

  10. #10
    eye kan kode gud jrandom's Avatar
    Join Date
    Dec 2009
    Location
    Seattle, WA
    Posts
    1,143
    For those of you following along with my mad ravings in this forum, altering the above multi-sampling routine to modify the spot coordinates in-place instead of copying the entire nodal_access structure results in a 1.04x increase in speed. Doesn't look like much on the surface, but translated to a 20-second-per-frame speed-up in my stress-test renders. This can make a huge difference when rendering animations.

    Code:
        // ---------------------------------------------------------------------------- Get_Alpha_Neighborhood()
        LWTypes::LWDVector Simplex_Noise::Get_Alpha_Neighborhood ( LWTypes::LWNodalAccess * nodal_access )
        {
            // NOTE: Is only accessed by Alpha_Bump, so is not cached. Calculate directly.
            
            // Determine offset of nearby coordinates
            const double epsilon = Neighborhod_Epsilon( nodal_access );
            
            
            // Generate the three neighborhood coordinates
            const LWTypes::LWDVector object_spot( nodal_access->oPos );
            const LWTypes::LWDVector world_spot ( nodal_access->wPos );
            
            LWTypes::LWDVector object_neighbor_X( object_spot.X() + epsilon, object_spot.Y(),           object_spot.Z()           );
            LWTypes::LWDVector object_neighbor_Y( object_spot.X(),           object_spot.Y() + epsilon, object_spot.Z()           );
            LWTypes::LWDVector object_neighbor_Z( object_spot.X(),           object_spot.Y(),           object_spot.Z() + epsilon );
            
            LWTypes::LWDVector world_neighbor_X ( world_spot.X()  + epsilon, world_spot.Y(),            world_spot.Z()            );
            LWTypes::LWDVector world_neighbor_Y ( world_spot.X(),            world_spot.Y()  + epsilon, world_spot.Z()            );
            LWTypes::LWDVector world_neighbor_Z ( world_spot.X(),            world_spot.Y(),            world_spot.Z()  + epsilon );
            
            
            // Backup existing spot locations
            double oPos_backup[3];
            double wPos_backup[3];
            
            memcpy( oPos_backup, nodal_access->oPos, sizeof(double) * 3 );
            memcpy( wPos_backup, nodal_access->wPos, sizeof(double) * 3 );
            
            
            // Sample alpha foreground and background at offset locations
            auto Update_Coordinates = [&]( LWTypes::LWDVector object_neighbor,
                                           LWTypes::LWDVector world_neighbor )
            {
                memcpy( nodal_access->oPos, (double*)object_neighbor, sizeof(double) * 3 );
                memcpy( nodal_access->wPos, (double*)world_neighbor,  sizeof(double) * 3 );
            };
            
            auto Get_Alpha_from_Offset = [&]( const Input_Scalar_t     & Input,
                                                    LWTypes::LWDVector   object_neighbor,
                                                    LWTypes::LWDVector   world_neighbor ) -> double
            {
                Update_Coordinates( object_neighbor, world_neighbor );
                
                return Input.Get( nodal_access );
            };
            
            const double alpha_fg_neighbor_X = Get_Alpha_from_Offset( Input_Alpha_Foreground, object_neighbor_X, world_neighbor_X );
            const double alpha_fg_neighbor_Y = Get_Alpha_from_Offset( Input_Alpha_Foreground, object_neighbor_Y, world_neighbor_Y );
            const double alpha_fg_neighbor_Z = Get_Alpha_from_Offset( Input_Alpha_Foreground, object_neighbor_Z, world_neighbor_Z );
            
            const double alpha_bg_neighbor_X = Get_Alpha_from_Offset( Input_Alpha_Background, object_neighbor_X, world_neighbor_X );
            const double alpha_bg_neighbor_Y = Get_Alpha_from_Offset( Input_Alpha_Background, object_neighbor_Y, world_neighbor_Y );
            const double alpha_bg_neighbor_Z = Get_Alpha_from_Offset( Input_Alpha_Background, object_neighbor_Z, world_neighbor_Z );
            
            
            // Restore spot locations
            memcpy( nodal_access->oPos, oPos_backup, sizeof(double) * 3 );
            memcpy( nodal_access->wPos, wPos_backup, sizeof(double) * 3 );
            
            
            // Get noise neighborhood for mixing amounts
            const Simplex_Neighborhood_t noise_neighborhood( Get_Noise_Neighborhood(nodal_access) );
    
            
            // Mix alpha amounts and return
            return LWTypes::LWDVector ( LERP( noise_neighborhood.Noise_X(), alpha_bg_neighbor_X, alpha_fg_neighbor_X ),
                                        LERP( noise_neighborhood.Noise_Y(), alpha_bg_neighbor_Y, alpha_fg_neighbor_Y ),
                                        LERP( noise_neighborhood.Noise_Z(), alpha_bg_neighbor_Z, alpha_fg_neighbor_Z ) );
        }

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •