Solved Sample a shader in 3D space in GVO


What's the proper way to sample a shader (noise, gradient, etc.) in 3D space without a given VolumeData object?

I understand how to map the ChannelData's p (uvw) parameter to my object's uvw coordinates, but how do I sample in a way that works like Mograph's shader effector works?

When I sample a noise, and put my objects uvw coordinates in the ChannelData object, if I switch the noise to "World Space", that doesn't work obviously, cause i'm still sampling in texture space in the code. Do you have to manually check that parameter in the BaseShader object?

See simplified code:

BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp* hh)
    GeData           myData;
    Bool             cacheDirty, dirty, frameUpdate;
    BaseObject       *outObject, *customShape;
    BaseShader       *shader;
    BaseDocument     *doc = hh->GetDocument();
    InitRenderStruct irs(doc);
    outObject = BaseObject::Alloc(Onull); // dummy output object
    cacheDirty = op->CheckCache(hh);
    op->GetParameter(DescLevel(FROMSHADER_FRAME_UPDATE), myData, DESCFLAGS_GET_0);
    frameUpdate = myData.GetBool();
    if (frameUpdate)
        Int32 curFrame = doc->GetTime().GetFrame(doc->GetFps());
        if (curFrame != prevFrame){
            dirty = true;
            prevFrame = curFrame;
    if (!dirty && !cacheDirty)
        return op->GetCache();
    op->GetParameter(DescLevel(FROMSHADER_SHADER), myData, DESCFLAGS_GET_0);
    shader = (BaseShader *)myData.GetLink(doc);
    if (!shader)
        goto error;
    customShape = BaseObject::Alloc(Ocube);
    customShape->SetParameter(DescLevel(PRIM_CUBE_LEN), GeData(Vector(shapeSize)), DESCFLAGS_SET_0);
    customShape = MakeEditable(customShape, doc, true);
    if (!customShape)
        goto error;
    if (shader->InitRender(irs) != INITRENDERRESULT_OK)
        goto error;
    if (!helper->grid)
        return false;
    StatusSetText("Sampling Noise");
    Vector rad = customShape->GetRad();
    for ( ; iter; ++iter)
        ChannelData cd; = 0;
        cd.scale = 0;
        cd.t = time;
        cd.texflag = CALC_TEXINFO(0, CHANNEL_COLOR);
        cd.d = Vector(1, 1, 1);
        cd.n = Vector(0, 1, 0);
        cd.vd = nullptr;
        Vector pos = iter.getPos();
        Vector worldPos = op->GetMg() * Vector(pos.x(), pos.y(), pos.z());
        Float min = 1 - ((rad.x + rad.y + rad.z) / 3);
        Float max = ((rad.x + rad.y + rad.z) / 3);
        cd.p = Vector((c4dPos.x - min) / (max - min),
                      (c4dPos.y - min) / (max - min),
                      (c4dPos.z - min) / (max - min));

        Vector col = shader->Sample(&cd);
        Float luma = 0.2126 * col.x + 0.7152 * col.y + 0.0722 * col.z; // calc luminence
    return outObject;
    return nullptr;

Hi Cody, thanks for writing us.

With regard to your question, passing a VolumeData instance when sampling a shader, although optional standing on the BaseShader::Sample() behavior, it's actually not especially with procedural shaders.
The MoGraph Shader Effector, on the contrary, uses a private VolumeData wrapper which is actually providing the data to be stored in the ChannelData used for BaseShader::Sample() thus resulting in a proper sampling of the noise shader.

That said I confirm that the proper sampling a procedural BaseShader is actually delivered only inside a VideoPostData-derived plugin where VolumeData instance is provided.

Best, Riccardo.

Thanks @r_gigante that's what I thought.