| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 1 | page.title=Graphics |
| 2 | parent.title=Renderscript |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 3 | parent.link=index.html |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 4 | |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 5 | @jd:body |
| 6 | |
| 7 | <div id="qv-wrapper"> |
| 8 | <div id="qv"> |
| 9 | <h2>In this document</h2> |
| 10 | |
| 11 | <ol> |
| 12 | <li> |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 13 | <a href="#creating-graphics-rs">Creating a Graphics Renderscript</a> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 14 | <ol> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 15 | <li><a href="#creating-native">Creating the Renderscript file</a></li> |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 16 | <li><a href="#creating-entry">Creating the Renderscript entry point class</a></li> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 17 | <li><a href="#creating-view">Creating the view class</a></li> |
| 18 | <li><a href="#creating-activity">Creating the activity class</a></li> |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 19 | </ol> |
| 20 | </li> |
| 21 | <li> |
| 22 | <a href="#drawing">Drawing</a> |
| 23 | <ol> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 24 | <li><a href="#drawing-rsg">Simple drawing</a></li> |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 25 | <li><a href="#drawing-mesh">Drawing with a mesh</a></li> |
| 26 | </ol> |
| 27 | </li> |
| 28 | <li> |
| 29 | <a href="#shaders">Shaders</a> |
| 30 | <ol> |
| 31 | <li><a href="#shader-bindings">Shader bindings</a></li> |
| 32 | <li><a href="#shader-sampler">Defining a sampler</a></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 33 | </ol> |
| 34 | </li> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 35 | <li> |
| 36 | <a href="#fbo">Rendering to a Framebuffer Object</a> |
| 37 | </li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 38 | </ol> |
| 39 | |
| 40 | <h2>Related Samples</h2> |
| 41 | |
| 42 | <ol> |
| 43 | <li><a href="{@docRoot}resources/samples/RenderScript/Balls/index.html">Balls</a></li> |
| 44 | |
| Scott Main | 19aad29 | 2011-10-18 16:57:32 -0700 | [diff] [blame] | 45 | <li><a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 46 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 47 | <li><a href="{@docRoot}resources/samples/RenderScript/FountainFbo/index.html">FountainFbo</a></li> |
| 48 | |
| 49 | <li><a href="{@docRoot}resources/samples/RenderScript/HelloWorld/index.html">Hello World</a></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 50 | |
| Scott Main | 19aad29 | 2011-10-18 16:57:32 -0700 | [diff] [blame] | 51 | <li><a |
| 52 | href="{@docRoot}resources/samples/RenderScript/MiscSamples/index.html">Misc Samples</a></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 53 | </ol> |
| 54 | </div> |
| 55 | </div> |
| 56 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 57 | <p>Renderscript provides a number of graphics APIs for rendering, both at the Android |
| 58 | framework level as well as at the Renderscript runtime level. For instance, the Android framework APIs let you |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 59 | create meshes and define shaders to customize the graphical rendering pipeline. The native |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 60 | Renderscript graphics APIs let you draw the actual meshes to render your scene. You need to |
| 61 | be familiar with both APIs to appropriately render graphics on an Android-powered device.</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 62 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 63 | <h2 id="creating-graphics-rs">Creating a Graphics Renderscript</h2> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 64 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 65 | <p>Renderscript applications require various layers of code, so it is useful to create the following |
| 66 | files to help keep your application organized:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 67 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 68 | <dl> |
| 69 | <dt>The Renderscript <code>.rs</code> file</dt> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 70 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 71 | <dd>This file contains the logic to do the graphics rendering.</dd> |
| 72 | |
| 73 | <dt>The Renderscript entry point <code>.java</code> class</dt> |
| 74 | |
| 75 | <dd>This class allows the view class to interact with the code defined in the <code>.rs</code> |
| 76 | file. This class contains a Renderscript object (instance of |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 77 | <code>ScriptC_<em>renderscript_file</em></code>), which allows your Android framework code to |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 78 | call the Renderscript code. In general, this class does much of the setup for Renderscript |
| 79 | such as shader and mesh building and memory allocation and binding. The SDK samples follow the |
| 80 | convention of naming this file ActivityRS.java, |
| 81 | where Activity is the name of your main activity class.</dd> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 82 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 83 | <dt>The view <code>.java</code> class</dt> |
| 84 | |
| 85 | <dd>This class extends {@link android.renderscript.RSSurfaceView} or {@link |
| 86 | android.renderscript.RSTextureView} to provide a surface to render on. A {@link |
| 87 | android.renderscript.RSSurfaceView} consumes a whole window, but a {@link |
| 88 | android.renderscript.RSTextureView} allows you to draw Renderscript graphics inside of a |
| 89 | view and add it to a {@link android.view.ViewGroup} alongside |
| 90 | other views. In this class, you create a {@link android.renderscript.RenderScriptGL} context object |
| 91 | with a call to {@link android.renderscript.RSSurfaceView#createRenderScriptGL |
| 92 | RSSurfaceView.createRenderscriptGL()} or {@link android.renderscript.RSTextureView#createRenderScriptGL |
| 93 | RSTextureView.createRenderscriptGL()}. The {@link android.renderscript.RenderScriptGL} context object |
| 94 | contains information about the current rendering state of Renderscript such as the vertex and |
| 95 | fragment shaders. You pass this context object to the Renderscript entry point class, so that |
| 96 | class can modify the rendering context if needed and bind the Renderscript code to the context. Once bound, |
| 97 | the view class can use the Renderscript code to display graphics. |
| 98 | The view class should also implement callbacks for events inherited from {@link |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 99 | android.view.View}, such as {@link android.view.View#onTouchEvent onTouchEvent()} and {@link |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 100 | android.view.View#onKeyDown onKeyDown()} if you want to detect these types of user interactions. |
| 101 | The SDK samples follow the convention of naming this file ActivityView.java, |
| 102 | where Activity is the name of your main activity class</dd> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 103 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 104 | <dt>The activity <code>.java</code> class</dt> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 105 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 106 | <dd>This class is the main activity class and sets your {@link android.renderscript.RSSurfaceView} as the main content |
| 107 | view for this activity or uses the {@link android.renderscript.RSTextureView} alongside other views.</dd> |
| 108 | </dl> |
| 109 | <p>Figure 1 describes how these classes interact with one another in a graphics Renderscript:</p> |
| 110 | |
| 111 | <img src="{@docRoot}images/rs_graphics.png"> |
| 112 | <p class="img-caption"><strong>Figure 1.</strong> Graphics Renderscript overview</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 113 | |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 114 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 115 | <p>The following sections describe how to create an application that uses a graphics Renderscript by using |
| 116 | the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Renderscript Fountain |
| 117 | sample</a> that is provided in the SDK as a guide (some code has been modified from its original |
| 118 | form for simplicity).</p> |
| 119 | |
| 120 | <h3 id="creating-native">Creating the Renderscript file</h3> |
| 121 | |
| 122 | <p>Your Renderscript code resides in <code>.rs</code> and <code>.rsh</code> (headers) files in the |
| 123 | <code><project_root>/src/</code> directory. This code contains the logic to render your |
| 124 | graphics and declares all other necessary items such as variables, structs, |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 125 | and pointers. Every graphics <code>.rs</code> file generally contains the following items:</p> |
| 126 | |
| 127 | <ul> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 128 | <li>A pragma declaration (<code>#pragma rs java_package_name(<em>package.name</em>)</code>) that declares |
| 129 | the package name of the <code>.java</code> reflection of this Renderscript.</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 130 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 131 | <li>A pragma declaration (<code>#pragma version(1)</code>) that declares the version of Renderscript that |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 132 | you are using (1 is the only value for now).</li> |
| 133 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 134 | <li>A <code>#include "rs_graphics.rsh"</code> declaration.</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 135 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 136 | <li>A <code>root()</code> function. This is the main worker function for your Renderscript and |
| 137 | calls Renderscript graphics functions to render scenes. This function is called every time a |
| 138 | frame refresh occurs, which is specified as its return value. A <code>0</code> (zero) specified for |
| 139 | the return value says to only render the frame when a property of the scene that you are |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 140 | rendering changes. A non-zero positive integer specifies the refresh rate of the frame in |
| 141 | milliseconds. |
| 142 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 143 | <p class="note"><strong>Note:</strong> The Renderscript runtime makes its best effort to |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 144 | refresh the frame at the specified rate. For example, if you are creating a live wallpaper |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 145 | and set the return value to 20, the Renderscript runtime renders the wallpaper at 20fps if it has just |
| 146 | enough or more resources to do so. It renders as fast as it can if not enough resources |
| 147 | are available.</p> |
| 148 | |
| 149 | <p>For more information on using the Renderscript graphics functions, see the <a href= |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 150 | "#drawing">Drawing</a> section.</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 151 | </li> |
| 152 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 153 | <li>An <code>init()</code> function. This allows you to do initialization of your |
| 154 | Renderscript before the <code>root()</code> function runs, such as assigning values to variables. This |
| 155 | function runs once and is called automatically when the Renderscript starts, before anything |
| 156 | else in your Renderscript. Creating this function is optional.</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 157 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 158 | <li>Any variables, pointers, and structures that you wish to use in your Renderscript code (can |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 159 | be declared in <code>.rsh</code> files if desired)</li> |
| 160 | </ul> |
| 161 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 162 | <p>The following code shows how the <code>fountain.rs</code> file is implemented:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 163 | <pre> |
| 164 | #pragma version(1) |
| 165 | |
| 166 | // Tell which java package name the reflected files should belong to |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 167 | #pragma rs java_package_name(com.example.android.rs.fountain) |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 168 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 169 | //declare shader binding |
| 170 | #pragma stateFragment(parent) |
| 171 | |
| 172 | // header with graphics APIs, must include explicitly |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 173 | #include "rs_graphics.rsh" |
| 174 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 175 | static int newPart = 0; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 176 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 177 | // the mesh to render |
| 178 | rs_mesh partMesh; |
| 179 | |
| 180 | // the point representing where a particle is rendered |
| 181 | typedef struct __attribute__((packed, aligned(4))) Point { |
| 182 | float2 delta; |
| 183 | float2 position; |
| 184 | uchar4 color; |
| 185 | } Point_t; |
| 186 | Point_t *point; |
| 187 | |
| 188 | // main worker function that renders particles onto the screen |
| 189 | int root() { |
| 190 | float dt = min(rsGetDt(), 0.1f); |
| 191 | rsgClearColor(0.f, 0.f, 0.f, 1.f); |
| 192 | const float height = rsgGetHeight(); |
| 193 | const int size = rsAllocationGetDimX(rsGetAllocation(point)); |
| 194 | float dy2 = dt * (10.f); |
| 195 | Point_t * p = point; |
| 196 | for (int ct=0; ct < size; ct++) { |
| 197 | p->delta.y += dy2; |
| 198 | p->position += p->delta; |
| 199 | if ((p->position.y > height) && (p->delta.y > 0)) { |
| 200 | p->delta.y *= -0.3f; |
| 201 | } |
| 202 | p++; |
| 203 | } |
| 204 | |
| 205 | rsgDrawMesh(partMesh); |
| 206 | return 1; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 207 | } |
| 208 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 209 | // adds particles to the screen to render |
| 210 | static float4 partColor[10]; |
| 211 | void addParticles(int rate, float x, float y, int index, bool newColor) |
| 212 | { |
| 213 | if (newColor) { |
| 214 | partColor[index].x = rsRand(0.5f, 1.0f); |
| 215 | partColor[index].y = rsRand(1.0f); |
| 216 | partColor[index].z = rsRand(1.0f); |
| 217 | } |
| 218 | float rMax = ((float)rate) * 0.02f; |
| 219 | int size = rsAllocationGetDimX(rsGetAllocation(point)); |
| 220 | uchar4 c = rsPackColorTo8888(partColor[index]); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 221 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 222 | Point_t * np = &point[newPart]; |
| 223 | float2 p = {x, y}; |
| 224 | while (rate--) { |
| 225 | float angle = rsRand(3.14f * 2.f); |
| 226 | float len = rsRand(rMax); |
| 227 | np->delta.x = len * sin(angle); |
| 228 | np->delta.y = len * cos(angle); |
| 229 | np->position = p; |
| 230 | np->color = c; |
| 231 | newPart++; |
| 232 | np++; |
| 233 | if (newPart >= size) { |
| 234 | newPart = 0; |
| 235 | np = &point[newPart]; |
| 236 | } |
| 237 | } |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 238 | } |
| 239 | </pre> |
| 240 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 241 | <h3 id="creating-entry">Creating the Renderscript entry point class</h3> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 242 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 243 | <p>When you create a Renderscript (<code>.rs</code>) file, it is helpful to create a |
| 244 | corresponding Android framework class that is an entry point into the <code>.rs</code> file. |
| 245 | The most important thing this class does is receive a {@link android.renderscript.RenderScriptGL} rendering context |
| 246 | object from the <a href="#creating-view">view class</a> and binds the actual Renderscript |
| 247 | code to the rendering context. This notifies your view class of the code that it needs |
| 248 | to render graphics. |
| 249 | </p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 250 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 251 | <p>In addition, this class should contain all of the things needed to set up Renderscript. |
| 252 | Some important things that you need to do in this class are:</p> |
| 253 | |
| 254 | <ul> |
| 255 | <li>Create a Renderscript object |
| 256 | <code>ScriptC_<em>rs_filename</em></code>. The Renderscript object is attached to the Renderscript bytecode, which is platform-independent and |
| 257 | gets compiled on the device when the Renderscript application runs. The bytecode is referenced |
| 258 | as a raw resource and is passed into the constructor for the Renderscript object. |
| 259 | For example, this is how the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a> |
| 260 | sample creates the Renderscript object: |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 261 | <pre> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 262 | RenderScriptGL rs; //obtained from the view class |
| 263 | Resources res; //obtained from the view class |
| 264 | ... |
| 265 | ScriptC_fountain mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain); |
| 266 | </pre> |
| 267 | </li> |
| 268 | <li>Allocate any necessary memory and bind it to your Renderscript code via the Renderscript object.</li> |
| 269 | <li>Build any necessary meshes and bind them to the Renderscript code via the Renderscript object.</li> |
| 270 | <li>Create any necessary programs and bind them to the Renderscript code via the Renderscript object.</li> |
| 271 | </ul> |
| 272 | |
| 273 | <p>The following code shows how the <a href= |
| 274 | "{@docRoot}resources/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/FountainRS.html"> |
| 275 | FountainRS</a> class is implemented:</p> |
| 276 | <pre> |
| 277 | package com.example.android.rs.fountain; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 278 | |
| 279 | import android.content.res.Resources; |
| 280 | import android.renderscript.*; |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 281 | import android.util.Log; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 282 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 283 | public class FountainRS { |
| 284 | public static final int PART_COUNT = 50000; |
| 285 | |
| 286 | public FountainRS() { |
| 287 | } |
| 288 | |
| 289 | /** |
| 290 | * This provides us with the Renderscript context and resources |
| 291 | * that allow us to create the Renderscript object |
| 292 | */ |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 293 | private Resources mRes; |
| 294 | private RenderScriptGL mRS; |
| 295 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 296 | // Renderscript object |
| 297 | private ScriptC_fountain mScript; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 298 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 299 | // Called by the view class to initialize the Renderscript context and renderer |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 300 | public void init(RenderScriptGL rs, Resources res) { |
| 301 | mRS = rs; |
| 302 | mRes = res; |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 303 | |
| 304 | /** |
| 305 | * Create a shader and bind to the Renderscript context |
| 306 | */ |
| 307 | ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs); |
| 308 | pfb.setVaryingColor(true); |
| 309 | rs.bindProgramFragment(pfb.create()); |
| 310 | |
| 311 | /** |
| 312 | * Allocate memory for the particles to render and create the mesh to draw |
| 313 | */ |
| 314 | ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT); |
| 315 | Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS); |
| 316 | smb.addVertexAllocation(points.getAllocation()); |
| 317 | smb.addIndexSetType(Mesh.Primitive.POINT); |
| 318 | Mesh sm = smb.create(); |
| 319 | |
| 320 | /** |
| 321 | * Create and bind the Renderscript object to the Renderscript context |
| 322 | */ |
| 323 | mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain); |
| 324 | mScript.set_partMesh(sm); |
| 325 | mScript.bind_point(points); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 326 | mRS.bindRootScript(mScript); |
| 327 | } |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 328 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 329 | boolean holdingColor[] = new boolean[10]; |
| 330 | |
| 331 | /** |
| 332 | * Calls Renderscript functions (invoke_addParticles) |
| 333 | * via the Renderscript object to add particles to render |
| 334 | * based on where a user touches the screen. |
| 335 | */ |
| 336 | public void newTouchPosition(float x, float y, float pressure, int id) { |
| 337 | if (id >= holdingColor.length) { |
| 338 | return; |
| 339 | } |
| 340 | int rate = (int)(pressure * pressure * 500.f); |
| 341 | if (rate > 500) { |
| 342 | rate = 500; |
| 343 | } |
| 344 | if (rate > 0) { |
| 345 | mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]); |
| 346 | holdingColor[id] = true; |
| 347 | } else { |
| 348 | holdingColor[id] = false; |
| 349 | } |
| 350 | |
| 351 | } |
| 352 | } |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 353 | </pre> |
| 354 | |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 355 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 356 | <h3 id="creating-view">Creating the view class</h3> |
| 357 | |
| 358 | |
| 359 | <p>To display graphics, you need a view to render on. Create a class that extends {@link |
| 360 | android.renderscript.RSSurfaceView} or {@link android.renderscript.RSTextureView}. This class |
| 361 | allows you to create a {@link android.renderscript.RenderScriptGL} context object by calling and |
| 362 | pass it to the Rendscript entry point class to bind the two. Once bound, the content is aware |
| 363 | of the code that it needs to use to render graphics with. If your Renderscript code |
| 364 | depends on any type of information that the view is aware of, such as touches from the user, |
| 365 | you can also use this class to relay that information to the Renderscript entry point class. |
| 366 | The following code shows how the <code>FountainView</code> class is implemented:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 367 | <pre> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 368 | package com.example.android.rs.fountain; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 369 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 370 | import android.renderscript.RSTextureView; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 371 | import android.renderscript.RenderScriptGL; |
| 372 | import android.content.Context; |
| 373 | import android.view.MotionEvent; |
| 374 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 375 | public class FountainView extends RSTextureView { |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 376 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 377 | public FountainView(Context context) { |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 378 | super(context); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 379 | } |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 380 | // Renderscript context |
| 381 | private RenderScriptGL mRS; |
| 382 | // Renderscript entry point object that calls Renderscript code |
| 383 | private FountainRS mRender; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 384 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 385 | /** |
| 386 | * Create Renderscript context and initialize Renderscript entry point |
| 387 | */ |
| 388 | @Override |
| 389 | protected void onAttachedToWindow() { |
| 390 | super.onAttachedToWindow(); |
| 391 | android.util.Log.e("rs", "onAttachedToWindow"); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 392 | if (mRS == null) { |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 393 | RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 394 | mRS = createRenderScriptGL(sc); |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 395 | mRender = new FountainRS(); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 396 | mRender.init(mRS, getResources()); |
| 397 | } |
| 398 | } |
| 399 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 400 | @Override |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 401 | protected void onDetachedFromWindow() { |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 402 | super.onDetachedFromWindow(); |
| 403 | android.util.Log.e("rs", "onDetachedFromWindow"); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 404 | if (mRS != null) { |
| 405 | mRS = null; |
| 406 | destroyRenderScriptGL(); |
| 407 | } |
| 408 | } |
| 409 | |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 410 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 411 | /** |
| 412 | * Use callbacks to relay data to Renderscript entry point class |
| 413 | */ |
| 414 | @Override |
| 415 | public boolean onTouchEvent(MotionEvent ev) |
| 416 | { |
| 417 | int act = ev.getActionMasked(); |
| 418 | if (act == ev.ACTION_UP) { |
| 419 | mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0)); |
| 420 | return false; |
| 421 | } else if (act == MotionEvent.ACTION_POINTER_UP) { |
| 422 | // only one pointer going up, we can get the index like this |
| 423 | int pointerIndex = ev.getActionIndex(); |
| 424 | int pointerId = ev.getPointerId(pointerIndex); |
| 425 | mRender.newTouchPosition(0, 0, 0, pointerId); |
| 426 | } |
| 427 | int count = ev.getHistorySize(); |
| 428 | int pcount = ev.getPointerCount(); |
| 429 | |
| 430 | for (int p=0; p < pcount; p++) { |
| 431 | int id = ev.getPointerId(p); |
| 432 | mRender.newTouchPosition(ev.getX(p), |
| 433 | ev.getY(p), |
| 434 | ev.getPressure(p), |
| 435 | id); |
| 436 | |
| 437 | for (int i=0; i < count; i++) { |
| 438 | mRender.newTouchPosition(ev.getHistoricalX(p, i), |
| 439 | ev.getHistoricalY(p, i), |
| 440 | ev.getHistoricalPressure(p, i), |
| 441 | id); |
| 442 | } |
| 443 | } |
| 444 | return true; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 445 | } |
| 446 | } |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 447 | </pre> |
| 448 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 449 | <h3 id="creating-activity">Creating the activity class</h3> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 450 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 451 | <p>Applications that use Renderscript still behave like normal Android applications, so you |
| 452 | need an activity class that handles activity lifecycle callback events appropriately. The activity class |
| 453 | also sets your {@link android.renderscript.RSSurfaceView} view class to be the main content view of the |
| 454 | activity or uses your {@link android.renderscript.RSTextureView} |
| 455 | in a {@link android.view.ViewGroup} alongside other views.</p> |
| 456 | |
| 457 | <p>The following code shows how the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a> |
| 458 | sample declares its activity class:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 459 | <pre> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 460 | package com.example.android.rs.fountain; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 461 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 462 | import android.app.Activity; |
| 463 | import android.os.Bundle; |
| 464 | import android.util.Log; |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 465 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 466 | public class Fountain extends Activity { |
| 467 | |
| 468 | private static final String LOG_TAG = "libRS_jni"; |
| 469 | private static final boolean DEBUG = false; |
| 470 | private static final boolean LOG_ENABLED = false; |
| 471 | |
| 472 | private FountainView mView; |
| 473 | |
| 474 | @Override |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 475 | public void onCreate(Bundle icicle) { |
| 476 | super.onCreate(icicle); |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 477 | |
| 478 | // Create our Preview view and set it as |
| 479 | // the content of our activity |
| 480 | mView = new FountainView(this); |
| 481 | setContentView(mView); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 482 | } |
| 483 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 484 | @Override |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 485 | protected void onResume() { |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 486 | Log.e("rs", "onResume"); |
| 487 | |
| 488 | // Ideally a game should implement onResume() and onPause() |
| 489 | // to take appropriate action when the activity looses focus |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 490 | super.onResume(); |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 491 | mView.resume(); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 492 | } |
| 493 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 494 | @Override |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 495 | protected void onPause() { |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 496 | Log.e("rs", "onPause"); |
| 497 | |
| 498 | // Ideally a game should implement onResume() and onPause() |
| 499 | // to take appropriate action when the activity looses focus |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 500 | super.onPause(); |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 501 | mView.pause(); |
| 502 | |
| 503 | } |
| 504 | |
| 505 | static void log(String message) { |
| 506 | if (LOG_ENABLED) { |
| 507 | Log.v(LOG_TAG, message); |
| 508 | } |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 509 | } |
| 510 | } |
| 511 | </pre> |
| 512 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 513 | <p>Now that you have an idea of what is involved in a Renderscript graphics application, you can |
| 514 | start building your own. It might be easiest to begin with one of the |
| 515 | <a href="{@docRoot}resources/samples/RenderScript/index.html">Renderscript samples</a> as a starting |
| 516 | point if this is your first time using Renderscript.</p> |
| 517 | |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 518 | <h2 id="drawing">Drawing</h2> |
| 519 | <p>The following sections describe how to use the graphics functions to draw with Renderscript.</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 520 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 521 | <h3 id="drawing-rsg">Simple drawing</h3> |
| 522 | |
| 523 | <p>The native Renderscript APIs provide a few convenient functions to easily draw a polygon or text to |
| 524 | the screen. You call these in your <code>root()</code> function to have them render to the {@link |
| 525 | android.renderscript.RSSurfaceView} or {@link android.renderscript.RSTextureView}. These functions are |
| 526 | available for simple drawing and should not be used for complex graphics rendering:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 527 | |
| 528 | <ul> |
| 529 | <li><code>rsgDrawRect()</code>: Sets up a mesh and draws a rectangle to the screen. It uses the |
| 530 | top left vertex and bottom right vertex of the rectangle to draw.</li> |
| 531 | |
| 532 | <li><code>rsgDrawQuad()</code>: Sets up a mesh and draws a quadrilateral to the screen.</li> |
| 533 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 534 | <li><code>rsgDrawQuadTexCoords()</code>: Sets up a mesh and draws a quadrilateral to the screen |
| 535 | using the provided coordinates of a texture.</li> |
| 536 | |
| 537 | <li><code>rsgDrawText()</code>: Draws specified text to the screen. Use <code>rsgFontColor()</code> |
| 538 | to set the color of the text.</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 539 | </ul> |
| 540 | |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 541 | <h3 id="drawing-mesh">Drawing with a mesh</h3> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 542 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 543 | <p>When you want to render complex scenes to the screen, instantiate a {@link |
| 544 | android.renderscript.Mesh} and draw it with <code>rsgDrawMesh()</code>. A {@link |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 545 | android.renderscript.Mesh} is a collection of allocations that represent vertex data (positions, |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 546 | normals, texture coordinates) and index data that provides information on how to draw triangles |
| 547 | and lines with the provided vertex data. You can build a Mesh in three different ways:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 548 | |
| 549 | <ul> |
| 550 | <li>Build the mesh with the {@link android.renderscript.Mesh.TriangleMeshBuilder} class, which |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 551 | allows you to specify a set of vertices and indices for each triangle that you want to draw.</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 552 | |
| 553 | <li>Build the mesh using an {@link android.renderscript.Allocation} or a set of {@link |
| 554 | android.renderscript.Allocation}s with the {@link android.renderscript.Mesh.AllocationBuilder} |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 555 | class. This approach allows you to build a mesh with vertices already stored in memory, which allows you |
| 556 | to specify the vertices in Renderscript or Android framework code.</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 557 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 558 | <li>Build the mesh with the {@link android.renderscript.Mesh.Builder} class. You should use |
| 559 | this convenience method when you know the data types you want to use to build your mesh, but |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 560 | don't want to make separate memory allocations like with {@link |
| 561 | android.renderscript.Mesh.AllocationBuilder}. You can specify the types that you want and this |
| 562 | mesh builder automatically creates the memory allocations for you.</li> |
| 563 | </ul> |
| 564 | |
| 565 | <p>To create a mesh using the {@link android.renderscript.Mesh.TriangleMeshBuilder}, you need to |
| 566 | supply it with a set of vertices and the indices for the vertices that comprise the triangle. For |
| 567 | example, the following code specifies three vertices, which are added to an internal array, |
| 568 | indexed in the order they were added. The call to {@link |
| 569 | android.renderscript.Mesh.TriangleMeshBuilder#addTriangle addTriangle()} draws the triangle with |
| 570 | vertex 0, 1, and 2 (the vertices are drawn counter-clockwise).</p> |
| 571 | <pre> |
| 572 | int float2VtxSize = 2; |
| 573 | Mesh.TriangleMeshBuilder triangle = new Mesh.TriangleMeshBuilder(renderscriptGL, |
| 574 | float2VtxSize, Mesh.TriangleMeshBuilder.COLOR); |
| 575 | triangles.addVertex(300.f, 300.f); |
| 576 | triangles.addVertex(150.f, 450.f); |
| 577 | triangles.addVertex(450.f, 450.f); |
| 578 | triangles.addTriangle(0 , 1, 2); |
| 579 | Mesh smP = triangle.create(true); |
| 580 | script.set_mesh(smP); |
| 581 | </pre> |
| 582 | |
| 583 | <p>To draw a mesh using the {@link android.renderscript.Mesh.AllocationBuilder}, you need to |
| 584 | supply it with one or more allocations that contain the vertex data:</p> |
| 585 | <pre> |
| 586 | Allocation vertices; |
| 587 | |
| 588 | ... |
| 589 | Mesh.AllocationBuilder triangle = new Mesh.AllocationBuilder(mRS); |
| 590 | smb.addVertexAllocation(vertices.getAllocation()); |
| 591 | smb.addIndexSetType(Mesh.Primitive.TRIANGLE); |
| 592 | Mesh smP = smb.create(); |
| 593 | script.set_mesh(smP); |
| 594 | </pre> |
| 595 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 596 | <p>In your Renderscript code, draw the built mesh to the screen:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 597 | <pre> |
| 598 | rs_mesh mesh; |
| 599 | ... |
| 600 | |
| 601 | int root(){ |
| 602 | ... |
| 603 | rsgDrawMesh(mesh); |
| 604 | ... |
| 605 | return 0; //specify a non zero, positive integer to specify the frame refresh. |
| 606 | //0 refreshes the frame only when the mesh changes. |
| 607 | } |
| 608 | </pre> |
| 609 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 610 | <h2 id="shader">Programs</h2> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 611 | |
| 612 | <p>You can attach four program objects to the {@link android.renderscript.RenderScriptGL} context |
| 613 | to customize the rendering pipeline. For example, you can create vertex and fragment shaders in |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 614 | GLSL or build a raster program object that controls culling. The four programs mirror a |
| 615 | traditional graphical rendering pipeline:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 616 | |
| 617 | <table> |
| 618 | <tr> |
| 619 | <th>Android Object Type</th> |
| 620 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 621 | <th>Renderscript Native Type</th> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 622 | |
| 623 | <th>Description</th> |
| 624 | </tr> |
| 625 | |
| 626 | <tr> |
| 627 | <td>{@link android.renderscript.ProgramVertex}</td> |
| 628 | |
| 629 | <td>rs_program_vertex</td> |
| 630 | |
| 631 | <td> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 632 | <p>The Renderscript vertex program, also known as a vertex shader, describes the stage in |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 633 | the graphics pipeline responsible for manipulating geometric data in a user-defined way. |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 634 | The object is constructed by providing Renderscript with the following data:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 635 | |
| 636 | <ul> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 637 | <li>An {@link android.renderscript.Element} describing its varying inputs or attributes</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 638 | |
| 639 | <li>GLSL shader string that defines the body of the program</li> |
| 640 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 641 | <li>a {@link android.renderscript.Type} that describes the layout of an |
| 642 | Allocation containing constant or uniform inputs</li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 643 | </ul> |
| 644 | |
| 645 | <p>Once the program is created, bind it to the {@link android.renderscript.RenderScriptGL} |
| 646 | graphics context by calling {@link android.renderscript.RenderScriptGL#bindProgramVertex |
| 647 | bindProgramVertex()}. It is then used for all subsequent draw calls until you bind a new |
| 648 | program. If the program has constant inputs, the user needs to bind an allocation |
| 649 | containing those inputs. The allocation's type must match the one provided during creation. |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 650 | </p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 651 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 652 | <p>The Renderscript runtime then does all the necessary plumbing to send those constants to |
| 653 | the graphics hardware. Varying inputs to the shader, such as position, normal, and texture |
| 654 | coordinates are matched by name between the input {@link android.renderscript.Element} |
| 655 | and the mesh object that is being drawn. The signatures don't have to be exact or in any |
| 656 | strict order. As long as the input name in the shader matches a channel name and size |
| 657 | available on the mesh, the Renderscript runtime handles connecting the two. Unlike OpenGL |
| 658 | there is no need to link the vertex and fragment programs.</p> |
| 659 | |
| 660 | <p>To bind shader constants to the program, declare a <code>struct</code> that contains the necessary |
| 661 | shader constants in your Renderscript code. This <code>struct</code> is generated into a |
| 662 | reflected class that you can use as a constant input element during the program's creation. |
| 663 | It is an easy way to create an instance of this <code>struct</code> as an allocation. You would then |
| 664 | bind this {@link android.renderscript.Allocation} to the program and the |
| 665 | Renderscript runtime sends the data that is contained in the <code>struct</code> to the hardware |
| 666 | when necessary. To update shader constants, you change the values in the |
| 667 | {@link android.renderscript.Allocation} and notify the Renderscript |
| 668 | code of the change.</p> |
| 669 | |
| 670 | <p>The {@link android.renderscript.ProgramVertexFixedFunction.Builder} class also |
| 671 | lets you build a simple vertex shader without writing GLSL code. |
| 672 | </p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 673 | </td> |
| 674 | </tr> |
| 675 | |
| 676 | <tr> |
| 677 | <td>{@link android.renderscript.ProgramFragment}</td> |
| 678 | |
| 679 | <td>rs_program_fragment</td> |
| 680 | |
| 681 | <td> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 682 | <p>The Renderscript fragment program, also known as a fragment shader, is responsible for |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 683 | manipulating pixel data in a user-defined way. It's constructed from a GLSL shader string |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 684 | containing the program body, texture inputs, and a {@link android.renderscript.Type} |
| 685 | object that describes the constants |
| 686 | used by the program. Like the vertex programs, when an {@link android.renderscript.Allocation} |
| 687 | with constant input |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 688 | values is bound to the shader, its values are sent to the graphics program automatically. |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 689 | Note that the values inside the {@link android.renderscript.Allocation} are not explicitly tracked. |
| 690 | If they change between two draw calls using the same program object, notify the runtime of that change by |
| 691 | calling <code>rsgAllocationSyncAll()</code>, so it can send the new values to hardware. Communication |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 692 | between the vertex and fragment programs is handled internally in the GLSL code. For |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 693 | example, if the fragment program is expecting a varying input called <code>varTex0</code>, the GLSL code |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 694 | inside the program vertex must provide it.</p> |
| 695 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 696 | <p>To bind shader constructs to the program, declare a <code>struct</code> that contains the necessary |
| 697 | shader constants in your Renderscript code. This <code>struct</code> is generated into a |
| 698 | reflected class that you can use as a constant input element during the program's creation. |
| 699 | It is an easy way to create an instance of this <code>struct</code> as an allocation. You would then |
| 700 | bind this {@link android.renderscript.Allocation} to the program and the |
| 701 | Renderscript runtime sends the data that is contained in the <code>struct</code> to the hardware |
| 702 | when necessary. To update shader constants, you change the values in the |
| 703 | {@link android.renderscript.Allocation} and notify the Renderscript |
| 704 | code of the change.</p> |
| 705 | |
| 706 | <p>The {@link android.renderscript.ProgramFragmentFixedFunction.Builder} class also |
| 707 | lets you build a simple fragment shader without writing GLSL code. |
| 708 | </p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 709 | </td> |
| 710 | </tr> |
| 711 | |
| 712 | <tr> |
| 713 | <td>{@link android.renderscript.ProgramStore}</td> |
| 714 | |
| 715 | <td>rs_program_store</td> |
| 716 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 717 | <td>The Renderscript store program contains a set of parameters that control how the graphics |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 718 | hardware writes to the framebuffer. It could be used to enable and disable depth writes and |
| 719 | testing, setup various blending modes for effects like transparency and define write masks |
| 720 | for color components.</td> |
| 721 | </tr> |
| 722 | |
| 723 | <tr> |
| 724 | <td>{@link android.renderscript.ProgramRaster}</td> |
| 725 | |
| 726 | <td>rs_program_raster</td> |
| 727 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 728 | <td>The Renderscript raster program is primarily used to specify whether point sprites are enabled and to |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 729 | control the culling mode. By default back faces are culled.</td> |
| 730 | </tr> |
| 731 | </table> |
| 732 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 733 | <p>The following example defines a vertex shader in GLSL and binds it to a Renderscript context object:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 734 | <pre> |
| 735 | private RenderScriptGL glRenderer; //rendering context |
| 736 | private ScriptField_Point mPoints; //vertices |
| 737 | private ScriptField_VpConsts mVpConsts; //shader constants |
| 738 | |
| 739 | ... |
| 740 | |
| 741 | ProgramVertex.Builder sb = new ProgramVertex.Builder(glRenderer); |
| 742 | String t = "varying vec4 varColor;\n" + |
| 743 | "void main() {\n" + |
| 744 | " vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" + |
| 745 | " pos.xy = ATTRIB_position;\n" + |
| 746 | " gl_Position = UNI_MVP * pos;\n" + |
| 747 | " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + |
| 748 | " gl_PointSize = ATTRIB_size;\n" + |
| 749 | "}\n"; |
| 750 | sb.setShader(t); |
| 751 | sb.addConstant(mVpConsts.getType()); |
| 752 | sb.addInput(mPoints.getElement()); |
| 753 | ProgramVertex pvs = sb.create(); |
| 754 | pvs.bindConstants(mVpConsts.getAllocation(), 0); |
| 755 | glRenderer.bindProgramVertex(pvs); |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 756 | </pre> |
| 757 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 758 | |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 759 | <p>The <a href= |
| 760 | "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html"> |
| 761 | RsRenderStatesRS</a> sample has many examples on how to create a shader without writing GLSL.</p> |
| 762 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 763 | <h3 id="shader-bindings">Program bindings</h3> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 764 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 765 | <p>You can also declare four pragmas that control default program bindings to the {@link |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 766 | android.renderscript.RenderScriptGL} context when the script is executing:</p> |
| 767 | |
| 768 | <ul> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 769 | <li><code>stateVertex</code></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 770 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 771 | <li><code>stateFragment</code></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 772 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 773 | <li><code>stateRaster</code></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 774 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 775 | <li><code>stateStore</code></li> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 776 | </ul> |
| 777 | |
| 778 | <p>The possible values for each pragma are <code>parent</code> or <code>default</code>. Using |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 779 | <code>default</code> binds the shaders to the graphical context with the system defaults.</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 780 | |
| 781 | <p>Using <code>parent</code> binds the shaders in the same manner as it is bound in the calling |
| 782 | script. If this is the root script, the parent state is taken from the bind points that are set |
| 783 | by the {@link android.renderscript.RenderScriptGL} bind methods.</p> |
| 784 | |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 785 | <p>For example, you can define this at the top of your graphics Renderscript code to have |
| 786 | the vertex and store programs inherent the bind properties from their parent scripts:</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 787 | <pre> |
| 788 | #pragma stateVertex(parent) |
| 789 | #pragma stateStore(parent) |
| 790 | </pre> |
| 791 | |
| Robert Ly | 8497593 | 2011-10-18 17:14:26 -0700 | [diff] [blame] | 792 | <h3 id="shader-sampler">Defining a sampler</h3> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 793 | |
| 794 | <p>A {@link android.renderscript.Sampler} object defines how data is extracted from textures. |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 795 | Samplers are bound to a {@link android.renderscript.ProgramFragment} alongside the texture |
| 796 | whose sampling they control. These |
| 797 | objects are used to specify such things as edge clamping behavior, whether mip-maps are used, and |
| 798 | the amount of anisotropy required. There might be situations where hardware does not support the |
| 799 | desired behavior of the sampler. In these cases, the Renderscript runtime attempts to provide the |
| 800 | closest possible approximation. For example, the user requested 16x anisotropy, but only 8x was |
| 801 | set because it's the best available on the hardware.</p> |
| Robert Ly | f197b63 | 2011-06-08 22:23:42 -0700 | [diff] [blame] | 802 | |
| 803 | <p>The <a href= |
| 804 | "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html"> |
| 805 | RsRenderStatesRS</a> sample has many examples on how to create a sampler and bind it to a |
| 806 | Fragment program.</p> |
| Robert Ly | b078d94 | 2012-01-05 10:50:39 -0800 | [diff] [blame] | 807 | |
| 808 | |
| 809 | |
| 810 | <h2 id="fbo">Rendering to a Framebuffer Object</h2> |
| 811 | |
| 812 | <p>Framebuffer objects allow you to render offscreen instead of in the default onscreen |
| 813 | framebuffer. This approach might be useful for situations where you need to post-process a texture before |
| 814 | rendering it to the screen, or when you want to composite two scenes in one such as rendering a rear-view |
| 815 | mirror of a car. There are two buffers associated with a framebuffer object: a color buffer |
| 816 | and a depth buffer. The color buffer (required) contains the actual pixel data of the scene |
| 817 | that you are rendering, and the depth buffer (optional) contains the values necessary to figure |
| 818 | out what vertices are drawn depending on their z-values.</p> |
| 819 | |
| 820 | <p>In general, you need to do the following to render to a framebuffer object:</p> |
| 821 | |
| 822 | <ul> |
| 823 | <li>Create {@link android.renderscript.Allocation} objects for the color buffer and |
| 824 | depth buffer (if needed). Specify the {@link |
| 825 | android.renderscript.Allocation#USAGE_GRAPHICS_RENDER_TARGET} usage attribute for these |
| 826 | allocations to notify the Renderscript runtime to use these allocations for the framebuffer |
| 827 | object. For the color buffer allocation, you most likely need to declare the {@link |
| 828 | android.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE} usage attribute |
| 829 | to use the color buffer as a texture, which is the most common use of the framebuffer object.</li> |
| 830 | |
| 831 | <li>Tell the Renderscript runtime to render to the framebuffer object instead of the default |
| 832 | framebuffer by calling <code>rsgBindColorTarget()</code> and passing it the color buffer |
| 833 | allocation. If applicable, call <code>rsgBindDepthTarget()</code> passing in the depth buffer |
| 834 | allocation as well.</li> |
| 835 | |
| 836 | <li>Render your scene normally with the <code>rsgDraw</code> functions. The scene will be |
| 837 | rendered into the color buffer instead of the default onscreen framebuffer.</li> |
| 838 | |
| 839 | <li>When done, tell the Renderscript runtime stop rendering to the color buffer and back |
| 840 | to the default framebuffer by calling <code>rsgClearAllRenderTargets()</code>.</li> |
| 841 | |
| 842 | <li>Create a fragment shader and bind a the color buffer to it as a texture.</li> |
| 843 | |
| 844 | <li>Render your scene to the default framebuffer. The texture will be used according |
| 845 | to the way you setup your fragment shader.</li> |
| 846 | </ul> |
| 847 | |
| 848 | <p>The following example shows you how to render to a framebuffer object by modifying the |
| 849 | <a href="{@docRoot}guide/resources/renderscript/Fountain/">Fountain</a> Renderscript sample. The end |
| 850 | result is the <a href="{@docRoot}guide/resources/renderscript/FountainFBO/">FountainFBO</a> sample. |
| 851 | The modifications render the exact same scene into a framebuffer object as it does the default |
| 852 | framebuffer. The framebuffer object is then rendered into the default framebuffer in a small |
| 853 | area at the top left corner of the screen.</p> |
| 854 | |
| 855 | <ol> |
| 856 | <li>Modify <code>fountain.rs</code> and add the following global variables. This creates setter |
| 857 | methods when this file is reflected into a <code>.java</code> file, allowing you to allocate |
| 858 | memory in your Android framework code and binding it to the Renderscript runtime. |
| 859 | <pre> |
| 860 | //allocation for color buffer |
| 861 | rs_allocation gColorBuffer; |
| 862 | //fragment shader for rendering without a texture (used for rendering to framebuffer object) |
| 863 | rs_program_fragment gProgramFragment; |
| 864 | //fragment shader for rendering with a texture (used for rendering to default framebuffer) |
| 865 | rs_program_fragment gTextureProgramFragment; |
| 866 | </pre> |
| 867 | </li> |
| 868 | |
| 869 | <li>Modify the root function of <code>fountain.rs</code> to look like the following code. The |
| 870 | modifications are commented: |
| 871 | <pre> |
| 872 | int root() { |
| 873 | float dt = min(rsGetDt(), 0.1f); |
| 874 | rsgClearColor(0.f, 0.f, 0.f, 1.f); |
| 875 | const float height = rsgGetHeight(); |
| 876 | const int size = rsAllocationGetDimX(rsGetAllocation(point)); |
| 877 | float dy2 = dt * (10.f); |
| 878 | Point_t * p = point; |
| 879 | for (int ct=0; ct < size; ct++) { |
| 880 | p->delta.y += dy2; |
| 881 | p->position += p->delta; |
| 882 | if ((p->position.y > height) && (p->delta.y > 0)) { |
| 883 | p->delta.y *= -0.3f; |
| 884 | } |
| 885 | p++; |
| 886 | } |
| 887 | //Tell Renderscript runtime to render to the frame buffer object |
| 888 | rsgBindColorTarget(gColorBuffer, 0); |
| 889 | //Begin rendering on a white background |
| 890 | rsgClearColor(1.f, 1.f, 1.f, 1.f); |
| 891 | rsgDrawMesh(partMesh); |
| 892 | |
| 893 | //When done, tell Renderscript runtime to stop rendering to framebuffer object |
| 894 | rsgClearAllRenderTargets(); |
| 895 | |
| 896 | //Bind a new fragment shader that declares the framebuffer object to be used as a texture |
| 897 | rsgBindProgramFragment(gTextureProgramFragment); |
| 898 | |
| 899 | //Bind the framebuffer object to the fragment shader at slot 0 as a texture |
| 900 | rsgBindTexture(gTextureProgramFragment, 0, gColorBuffer); |
| 901 | //Draw a quad using the framebuffer object as the texture |
| 902 | float startX = 10, startY = 10; |
| 903 | float s = 256; |
| 904 | rsgDrawQuadTexCoords(startX, startY, 0, 0, 1, |
| 905 | startX, startY + s, 0, 0, 0, |
| 906 | startX + s, startY + s, 0, 1, 0, |
| 907 | startX + s, startY, 0, 1, 1); |
| 908 | |
| 909 | //Rebind the original fragment shader to render as normal |
| 910 | rsgBindProgramFragment(gProgramFragment); |
| 911 | |
| 912 | //Render the main scene |
| 913 | rsgDrawMesh(partMesh); |
| 914 | |
| 915 | return 1; |
| 916 | } |
| 917 | </pre> |
| 918 | </li> |
| 919 | |
| 920 | <li>In the <code>FountainRS.java</code> file, modify the <code>init()</code> method to look |
| 921 | like the following code. The modifications are commented: |
| 922 | |
| 923 | <pre> |
| 924 | /* Add necessary members */ |
| 925 | private ScriptC_fountainfbo mScript; |
| 926 | private Allocation mColorBuffer; |
| 927 | private ProgramFragment mProgramFragment; |
| 928 | private ProgramFragment mTextureProgramFragment; |
| 929 | |
| 930 | public void init(RenderScriptGL rs, Resources res) { |
| 931 | mRS = rs; |
| 932 | mRes = res; |
| 933 | |
| 934 | ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT); |
| 935 | |
| 936 | Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS); |
| 937 | smb.addVertexAllocation(points.getAllocation()); |
| 938 | smb.addIndexSetType(Mesh.Primitive.POINT); |
| 939 | Mesh sm = smb.create(); |
| 940 | |
| 941 | mScript = new ScriptC_fountainfbo(mRS, mRes, R.raw.fountainfbo); |
| 942 | mScript.set_partMesh(sm); |
| 943 | mScript.bind_point(points); |
| 944 | |
| 945 | ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs); |
| 946 | pfb.setVaryingColor(true); |
| 947 | mProgramFragment = pfb.create(); |
| 948 | mScript.set_gProgramFragment(mProgramFragment); |
| 949 | |
| 950 | /* Second fragment shader to use a texture (framebuffer object) to draw with */ |
| 951 | pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, |
| 952 | ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); |
| 953 | |
| 954 | /* Set the fragment shader in the Renderscript runtime */ |
| 955 | mTextureProgramFragment = pfb.create(); |
| 956 | mScript.set_gTextureProgramFragment(mTextureProgramFragment); |
| 957 | |
| 958 | /* Create the allocation for the color buffer */ |
| 959 | Type.Builder colorBuilder = new Type.Builder(mRS, Element.RGBA_8888(mRS)); |
| 960 | colorBuilder.setX(256).setY(256); |
| 961 | mColorBuffer = Allocation.createTyped(mRS, colorBuilder.create(), |
| 962 | Allocation.USAGE_GRAPHICS_TEXTURE | |
| 963 | Allocation.USAGE_GRAPHICS_RENDER_TARGET); |
| 964 | |
| 965 | /* Set the allocation in the Renderscript runtime */ |
| 966 | mScript.set_gColorBuffer(mColorBuffer); |
| 967 | |
| 968 | mRS.bindRootScript(mScript); |
| 969 | } |
| 970 | </pre> |
| 971 | |
| 972 | <p class="note"><strong>Note:</strong> This sample doesn't use a depth buffer, but the following code |
| 973 | shows you how to declare an example depth buffer if you need to use |
| 974 | one for your application. The depth buffer must have the same dimensions as the color buffer: |
| 975 | |
| 976 | <pre> |
| 977 | Allocation mDepthBuffer; |
| 978 | |
| 979 | ... |
| 980 | |
| 981 | Type.Builder b = new Type.Builder(mRS, Element.createPixel(mRS, DataType.UNSIGNED_16, |
| 982 | DataKind.PIXEL_DEPTH)); |
| 983 | b.setX(256).setY(256); |
| 984 | mDepthBuffer = Allocation.createTyped(mRS, b.create(), |
| 985 | Allocation.USAGE_GRAPHICS_RENDER_TARGET); |
| 986 | |
| 987 | </pre> |
| 988 | </p> |
| 989 | </li> |
| 990 | |
| 991 | <li>Run and use the sample. The smaller, white quad on the top-left corner is using the |
| 992 | framebuffer object as a texture, which renders the same scene as the main rendering.</li> |
| 993 | </ol> |