Skip to content

OIIO image reader plugin that runs OSL shaders to make the image dynamically#531

Merged
lgritz merged 2 commits intoAcademySoftwareFoundation:masterfrom
lgritz:lg-oslimage
Jun 16, 2015
Merged

OIIO image reader plugin that runs OSL shaders to make the image dynamically#531
lgritz merged 2 commits intoAcademySoftwareFoundation:masterfrom
lgritz:lg-oslimage

Conversation

@lgritz
Copy link
Collaborator

@lgritz lgritz commented Jun 13, 2015

OIIO (and any app that uses it to read images) not only can read many image formats, but if it doesn't know a format internally, it will look for a plug-in that can read the format. But it's just a black box, it doesn't HAVE to actually read an image from disk. It could do something procedural.

So... I wrote an OIIO image reading plugin that executes OSL code to procedurally generate image pixels as needed. It can do some fun tricks.

Generate an image by running a compiled OSL shader:

shader ramp (
    color topleft     = color(0,0,0),
    color topright    = color(1,0,0),
    color bottomleft  = color(0,1,0),
    color bottomright = color(1,1,1),
    output color result = 0,
    output float alpha = 1
    )
{
    result = topleft*(1-u)*(1-v) + topright*u*(1-v) +
             bottomleft*(1-u)*v + bottomright*u*v;
}

$ oslc ramp.osl
$ oiiotool ramp.oso -o ramp.exr

You can generate directly into an iv window (or any other app that uses OIIO to read):

iv ramp.oso

A ".oslgroup" file can contain the serialization of an entire shading network:

oiiotool network.oslgroup -o complex.exr

URI notation can be used to set shader parameters:

oiiotool "fBm.oso?frequency=4&octaves=4&point offset=0,1,0" -o noise.exr

URI notation also to control the resolution of the "image", or to make it look tiled rather than scanline, or even to make it behave like it's MIP-mapped:

iv "fBm.oso?octaves=4&RES=512x512&MIP=1&TILE=64x64"

".oslbody" signifies a snipped of OSL that will be pasted into OSL shader boilerplate, compiled from memory, and JITed on the fly:

iv "result = noise (u*5, v*5);.oslbody"

Not just a single expression, you could write a whole program if you want:

iv "int octaves=8; float freq=2,amp=0.5;\
    for (int i = 0; i < octaves; ++i, amp /=2, freq*=2) \
        result += amp*(color)snoise(freq*u,freq*v); \
    result += 0.5; \
   .oslbody?RES=512x512"

Yeah, crazy.

I'm starting to imagine reasons for an app to have multiple "renderers"
using different RendererServices subclasses. Each one should be allowed
to hold a distinct TextureSystem, which it couldn't before. The only
obvious way to do this is holding the pointer in the RS object itself.
@fpsunflower
Copy link
Contributor

Very cool! How many loose ends are left?

Is it possible to access textures in these types of plugins (not to mention the possibility of having texture calls that invoke this loader recursively ...)?

@lgritz
Copy link
Collaborator Author

lgritz commented Jun 15, 2015

The one-day version was very rough -- read_scanline only (now it can do scanline, scanlines, tile, or tiles), the parameter parsing/setting was crude and could only handle simple int or float (now accepts types so you could set points, colors, arrays, strings, whatever), had no error handling and would crash if you specified things wrong (now is reasonably stable and will even bubble up most error messages properly), did not support the MIP=1 flag to make it look mipmapped.

All the loose ends on my original list got tied up by the time I submitted the PR.

You can access textures in these plugins, that seems to be fine. (But I urge you not to make a texture name that causes infinite recursion!)

I think the only think I haven't tested, maybe I'll get to it today, is fully trying to make one of these special names for the renderer itself, and make sure that all works.

@fpsunflower
Copy link
Contributor

LGTM

OIIO (and any app that uses it to read images) not only can read many
image formats, but if it doesn't know a format internally, it will look
for a plug-in that can read the format. But it's just a black box, it
doesn't HAVE to actually read an image from disk. It could do something
procedural.

So... I wrote an OIIO image reading plugin that executes OSL code to
procedurally generate image pixels as needed. It can do some fun tricks.

Generate an image by running a compiled OSL shader:

    shader ramp (
        color topleft     = color(0,0,0),
        color topright    = color(1,0,0),
        color bottomleft  = color(0,1,0),
        color bottomright = color(1,1,1),
        output color result = 0,
        output float alpha = 1
        )
    {
        result = topleft*(1-u)*(1-v) + topright*u*(1-v) +
                 bottomleft*(1-u)*v + bottomright*u*v;
    }

    $ oslc ramp.osl
    $ oiiotool ramp.oso -o ramp.exr

You can generate directly into an iv window (or any other app that
uses OIIO to read):

    iv ramp.oso

A ".oslgroup" file can contain the serialization of an entire shading
network:

    oiiotool network.oslgroup -o complex.exr

URI notation can be used to set shader parameters:

    oiiotool "fBm.oso?frequency=4&octaves=4&point offset=0,1,0" -o noise.exr

URI notation also to control the resolution of the "image", or to make
it look tiled rather than scanline, or even to make it behave like it's
MIP-mapped:

    iv "fBm.oso?octaves=4?RES=512x512&MIP=1&TILE=64x64"

".oslbody" signifies a snipped of OSL that will be pasted into
OSL shader boilerplate, compiled from memory, and JITed on the fly:

    iv "result = noise (u*5, v*5);.oslbody"

Not just a single expression, you could write a whole program if you
want:

    iv "int octaves=8; float freq=2,amp=0.5; for (int i = 0; i < octaves; ++i, amp /=2, freq*=2)result += amp*(color)snoise(freq*u,freq*v); result += 0.5; .oslbody?RES=512x512"

Yeah, crazy.
@lgritz
Copy link
Collaborator Author

lgritz commented Jun 16, 2015

Haha, tested this in our renderer and it DOES work! Though you do need to make it look mipped and tiled or the renderer is instructed to reject it as an error.

Chris et al, alter test_0693/test.ass from

commands "string filename checkers.tif ; shader simple layer1;"

to

commands "string filename \"result=noise(10*s,t0*t);.oslbody?MIP=1&TILE=64\" ; shader simple layer1;"

and it actually works!

@lgritz lgritz merged commit 1e3f41e into AcademySoftwareFoundation:master Jun 16, 2015
@lgritz lgritz deleted the lg-oslimage branch June 16, 2015 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants