Beispiel #1
0
void
OceanSurfaceNode::rebuildShaders()
{
    // need the terrain engine so we can get at the compositor.
    TerrainEngineNode* engine = osgEarth::findTopMostNodeOfType<TerrainEngineNode>( this );
    if ( !engine ) {
        OE_DEBUG << LC << "No terrain engine found in the map node; abort" << std::endl;
        return;
    }

    // access the compositor because we are going to be sampling map layers.
    TextureCompositor* comp = engine->getTextureCompositor();
    if ( !comp ) {
        OE_INFO << LC << "No texture compositor found in the terrain engine; abort" << std::endl;
        return;
    }

    // reserve a texture unit for the surface texture (if we haven't already)
    if ( !_oceanSurfaceTextureApplied && _oceanSurfaceTextureUnit < 0 && _oceanSurfaceTexture.valid() )
    {
        if ( comp->reserveTextureImageUnit( _oceanSurfaceTextureUnit ) )
        {
            getOrCreateStateSet()->setTextureAttributeAndModes(
                _oceanSurfaceTextureUnit, _oceanSurfaceTexture.get(), osg::StateAttribute::ON);
            _oceanSurfaceTextureApplied = true;
        }
        else
        {
            OE_WARN << LC << "Sorry, failed to allocate a texture image unit for the surface texture." << std::endl;
        }
    }

    // create a VP to store our custom shader components.
    osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
    getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );

    // if the ocean is disabled, just return without injecting any shaders.
    if ( !_enabled )
        return;

    // build the sampler function if necessary
    osg::ref_ptr<const ImageLayer> safeMaskLayer = _maskLayer.get();
    osg::Shader* maskSampler = 0L;
    if ( safeMaskLayer.valid() )
    {
        maskSampler = comp->createSamplerFunction( safeMaskLayer->getUID(), MASK_SAMPLER_FUNC, osg::Shader::VERTEX );        
        if ( maskSampler )
            vp->setShader( MASK_SAMPLER_FUNC, maskSampler );
    }

    // make the helper functions.
    {
        std::stringstream buf;

        buf << "vec3 xyz_to_lat_lon_height(in vec3 xyz) \n"
            << "{ \n"
            << "    float X = xyz.x;\n"
            << "    float Y = xyz.y;\n"
            << "    float Z = xyz.z;\n"
            << "    float _radiusEquator = 6378137.0;\n"
            << "    float _radiusPolar   = 6356752.3142;\n"
            << "    float flattening = (_radiusEquator-_radiusPolar)/_radiusEquator;\n"
            << "    float _eccentricitySquared = 2.0*flattening - flattening*flattening;\n"
            << "    float p = sqrt(X*X + Y*Y);\n"
            << "    float theta = atan(Z*_radiusEquator , (p*_radiusPolar));\n"
            << "    float eDashSquared = (_radiusEquator*_radiusEquator - _radiusPolar*_radiusPolar)/(_radiusPolar*_radiusPolar);\n"
            << "    float sin_theta = sin(theta);\n"
            << "    float cos_theta = cos(theta);\n"
            << "    float latitude = atan( (Z + eDashSquared*_radiusPolar*sin_theta*sin_theta*sin_theta), (p - _eccentricitySquared*_radiusEquator*cos_theta*cos_theta*cos_theta) );\n"
            << "    float longitude = atan(Y,X);\n"
            << "    float sin_latitude = sin(latitude);\n"
            << "    float N = _radiusEquator / sqrt( 1.0 - _eccentricitySquared*sin_latitude*sin_latitude);\n"
            << "    float height = p/cos(latitude) - N;\n"
            << "    return vec3(longitude, latitude, height);\n"
            << "} \n"
            << "\n";

        std::string str = buf.str();
        vp->setShader( "xyz_to_lat_lon_height", new osg::Shader(osg::Shader::VERTEX, str) );
    }

    // next make the vertex shader function that will morph the ocean verts and prepare
    // the texture coordinates for the surface effects.
    {
        std::stringstream buf;

        buf << std::fixed;

        buf << "uniform float osg_SimulationTime; \n"
            << "uniform mat4  osg_ViewMatrixInverse;\n"
            << "uniform mat4  osg_ViewMatrix;\n"
            << "uniform float osgearth_OceanHeight;\n"
            << "uniform float osgearth_OceanPeriod;\n"
            << "uniform float osgearth_OceanAnimationPeriod;\n"
            << "varying float osgearth_OceanAlpha;\n"
            << "varying float osgearth_CameraRange; \n"

            << "vec3 xyz_to_lat_lon_height(in vec3 xyz); \n";

        if ( _oceanSurfaceTextureApplied )
        {
            buf << "varying vec3 osgearth_oceanSurfaceTexCoord; \n";
        }

        if ( maskSampler )
        {
            buf << "vec4 " << MASK_SAMPLER_FUNC << "(); \n";
        }

        buf << "void osgearth_ocean_morphSurface() \n"
            << "{ \n"
            << "   mat4 modelMatrix = osg_ViewMatrixInverse * gl_ModelViewMatrix; \n"
            << "   vec4 vert = modelMatrix  * gl_Vertex; \n"           
            << "   vec3 vert3 = vec3(vert.x, vert.y, vert.z); \n"
            << "   vec3 latlon = xyz_to_lat_lon_height(vert3); \n"
            << "   osgearth_OceanAlpha = 1.0; \n";

        if ( maskSampler )
        {
            buf << "    osgearth_OceanAlpha = 1.0 - (" << MASK_SAMPLER_FUNC << "()).a; \n";
        }

        if ( _invertMask )
            buf << "    osgearth_OceanAlpha = 1.0 - osgearth_OceanAlpha; \n";

        buf << "   if ( osgearth_CameraRange <= " << _maxRange << " ) \n"
            << "   { \n"
            << "       float s = mix(1.0, 0.0, osgearth_CameraRange / " << _maxRange << "); \n" //Invert so it's between 0 and 1
            << "       osgearth_OceanAlpha *= s; \n"
            << "   } \n"
            << "   else \n"
            << "   { \n"
            << "        osgearth_OceanAlpha = 0.0; \n"
            << "   } \n"

            << "   if (osgearth_OceanAlpha > 0.0) \n"
            << "   { \n"
            << "       float PI_2 = 3.14158 * 2.0; \n"
            << "       float period = PI_2/osgearth_OceanPeriod; \n"
            << "       float half_period = period / 2.0; \n"
            << "       vec3 n = normalize(vert3);\n"
            << "       float theta = (mod(latlon.x, period) / period) * PI_2; \n"  
            << "       float phi = (mod(latlon.y, half_period) / half_period) * PI_2; \n"
            << "       float phase1 = osg_SimulationTime * 2.0; \n"
            << "       float phase2 = osg_SimulationTime * 4.0; \n"
            << "       float waveHeight = (osgearth_OceanAlpha) * osgearth_OceanHeight; \n"
            << "       float scale1 = sin(theta + phase1) * waveHeight; \n"
            << "       float scale2 = cos(phi + phase2) * waveHeight; \n"
            << "       float scale3 = sin(theta + phase2) * cos(phi + phase1) * waveHeight * 1.6; \n"
            << "       float scale = (scale1 + scale2 + scale3)/3.0; \n";

        // flatten verts to MSL:
        if ( _adjustToMSL )
        {
            buf << "        vec3 offset = n * -latlon.z; \n"
                << "        vert += vec4( offset.xyz, 0 ); \n";
        }

        // apply the save scale:
        buf << "       n = n * scale; \n"
            << "       vert += vec4(n.x, n.y,n.z,0); \n"
            << "       vert = osg_ViewMatrix * vert; \n"
            << "       gl_Position = gl_ProjectionMatrix * vert; \n"
            << "   }\n";

        // set up the coords for the surface texture:
        if ( _oceanSurfaceTextureApplied )
        {
            buf << "   osgearth_oceanSurfaceTexCoord.x =  latlon.x / " << _oceanSurfaceImageSizeRadians << "; \n"
                << "   osgearth_oceanSurfaceTexCoord.y =  latlon.y / " << _oceanSurfaceImageSizeRadians << "; \n"
                << "   osgearth_oceanSurfaceTexCoord.z = fract(osg_SimulationTime/osgearth_OceanAnimationPeriod); \n";
        }

        buf << "}\n";

        // add as a custom user function in the shader composition:
        std::string vertSource = buf.str();
        vp->setFunction( "osgearth_ocean_morphSurface", vertSource, osgEarth::ShaderComp::LOCATION_VERTEX_PRE_TEXTURING );
    }
    
    // now we need a fragment function that will apply the ocean surface texture.
    if ( _oceanSurfaceTextureApplied )
    {
        getOrCreateStateSet()->getOrCreateUniform( "osgearth_oceanSurfaceTex", osg::Uniform::SAMPLER_3D )->set( _oceanSurfaceTextureUnit );

        std::stringstream buf;

        buf << "uniform sampler3D osgearth_oceanSurfaceTex; \n"
            << "varying vec3      osgearth_oceanSurfaceTexCoord; \n"
            << "varying float     osgearth_OceanAlpha; \n"

            << "void osgearth_ocean_applySurfaceTex( inout vec4 color ) \n"
            << "{ \n"
            << "    vec4 texel = texture3D(osgearth_oceanSurfaceTex, osgearth_oceanSurfaceTexCoord); \n"
            << "    color = vec4( mix( color.rgb, texel.rgb, texel.a * osgearth_OceanAlpha ), color.a); \n"
            << "} \n";

        std::string str = buf.str();
        vp->setFunction( "osgearth_ocean_applySurfaceTex", str, osgEarth::ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
    }
}
Beispiel #2
0
bool
ShadowUtils::setUpShadows(osgShadow::ShadowedScene* sscene, osg::Group* root)
{
    osg::StateSet* ssStateSet = sscene->getOrCreateStateSet();

    MapNode* mapNode = MapNode::findMapNode(root);
    TerrainEngineNode* engine = mapNode->getTerrainEngine();
    if (!engine)
        return false;

    TextureCompositor* compositor = engine->getTextureCompositor();
    int su = -1;
    if (!compositor->reserveTextureImageUnit(su))
        return false;

    OE_INFO << LC << "Reserved texture unit " << su << " for shadowing" << std::endl;

    osgShadow::ViewDependentShadowMap* vdsm =  dynamic_cast< osgShadow::ViewDependentShadowMap*>(sscene->getShadowTechnique());
    int su1 = -1;
    if (vdsm && sscene->getShadowSettings()->getNumShadowMapsPerLight() == 2)
    {
        if (!compositor->reserveTextureImageUnit(su1) || su1 != su + 1)
        {
            OE_FATAL << LC << "couldn't get contiguous shadows for split vdsm\n";
            sscene->getShadowSettings()->setNumShadowMapsPerLight(1);
            if (su1 != -1)
                compositor->releaseTextureImageUnit(su1);
            su1 = -1;
        }
        else
        {
            OE_INFO << LC << "Reserved texture unit " << su1 << " for shadowing" << std::endl;
        }
    }

    // create a virtual program to attach to the shadowed scene.
    VirtualProgram* vp = new VirtualProgram();
    vp->setName( "shadow:terrain" );
    //vp->installDefaultColoringAndLightingShaders();

    ssStateSet->setAttributeAndModes( vp, 1 );


    std::stringstream buf;
    buf << "#version " << GLSL_VERSION_STR << "\n";
#ifdef OSG_GLES2_AVAILABLE
    buf << "precision mediump float;\n";
#endif
    buf << "varying vec4 oe_shadow_ambient;\n";
    buf << "varying vec4 oe_shadow_TexCoord0;\n";
    if ( su1 >= 0 )
        buf << "varying vec4 oe_shadow_TexCoord1;\n";


    buf << "void oe_shadow_setupShadowCoords(inout vec4 VertexVIEW)\n";
    buf << "{\n";
    buf << "    vec4 position4 = VertexVIEW;\n";
    buf << "    oe_shadow_TexCoord0.s = dot( position4, gl_EyePlaneS[" << su <<"]);\n";
    buf << "    oe_shadow_TexCoord0.t = dot( position4, gl_EyePlaneT[" << su <<"]);\n";
    buf << "    oe_shadow_TexCoord0.p = dot( position4, gl_EyePlaneR[" << su <<"]);\n";
    buf << "    oe_shadow_TexCoord0.q = dot( position4, gl_EyePlaneQ[" << su <<"]);\n";
    if (su1 >= 0)
    {
        buf << "    oe_shadow_TexCoord1.s = dot( position4, gl_EyePlaneS[" << su1 <<"]);\n";
        buf << "    oe_shadow_TexCoord1.t = dot( position4, gl_EyePlaneT[" << su1 <<"]);\n";
        buf << "    oe_shadow_TexCoord1.p = dot( position4, gl_EyePlaneR[" << su1 <<"]);\n";
        buf << "    oe_shadow_TexCoord1.q = dot( position4, gl_EyePlaneQ[" << su1 <<"]);\n";
    }

    // the ambient lighting will control the intensity of the shadow.
    buf << "    oe_shadow_ambient = gl_FrontLightProduct[0].ambient; \n"
        << "}\n";

    std::string setupShadowCoords;
    setupShadowCoords = buf.str();

    vp->setFunction(
        "oe_shadow_setupShadowCoords", 
        setupShadowCoords, 
        ShaderComp::LOCATION_VERTEX_VIEW,
        -1.0 );

    std::stringstream buf2;
    buf2 <<
        "#version " << GLSL_VERSION_STR << "\n"
#ifdef OSG_GLES2_AVAILABLE
        "precision mediump float;\n"
#endif
        "uniform sampler2DShadow shadowTexture;\n"
        "varying vec4 oe_shadow_TexCoord0;\n";

    if (su1 >= 0)
    {
        // bound by vdsm
        buf2 << "uniform sampler2DShadow shadowTexture1;\n";
        buf2 << "varying vec4 oe_shadow_TexCoord1;\n";
    }
    buf2 <<
        "varying vec4 oe_shadow_ambient;\n"

        "void oe_shadow_applyLighting( inout vec4 color )\n"
        "{\n"
        "    float alpha = color.a;\n"
        "    float shadowFac = shadow2DProj( shadowTexture, oe_shadow_TexCoord0).r;\n";
    if (su1 > 0)
    {
        buf2 << "    shadowFac *= shadow2DProj( shadowTexture1, oe_shadow_TexCoord1).r;\n";
    }

    // calculate the shadowed color and mix if with the lit color based on the
    // ambient lighting. The 0.5 is a multiplier that darkens the shadow in
    // proportion to ambient light. It should probably be a uniform.
    buf2 <<
        "    vec4 colorInFullShadow = color * oe_shadow_ambient; \n"
        "    color = mix(colorInFullShadow, color, shadowFac); \n"
        "    color.a = alpha;\n"
        "}\n";

    std::string fragApplyLighting;
    fragApplyLighting = buf2.str();

    vp->setFunction(
        "oe_shadow_applyLighting",
        fragApplyLighting,
        osgEarth::ShaderComp::LOCATION_FRAGMENT_LIGHTING );

    setShadowUnit(sscene, su);

    // VDSM uses a different sampler name, shadowTexture0.
    ssStateSet
        ->getOrCreateUniform("shadowTexture", osg::Uniform::SAMPLER_2D_SHADOW)
        ->set(su);

    return true;
}