Home > Articles

Rendering Using Simple Techniques

This chapter is from the book

In This Chapter

  • Using Vertex Buffers
  • Texturing Our Objects
  • In Brief

The rendering done up to this point hasn't been done very efficiently. New lists of vertices were allocated every time the scene was rendered, and everything was stored in system memory. With modern graphics cards having an abundance of memory built into the card, you can get vast performance improvements by storing your vertex data in the video memory of the card. Having the vertex data stored in system memory requires copying the data from the system to the video card every time the scene will be rendered, and this copying of data can be quite time consuming. Removing the allocation from every frame could only help as well.

Using Vertex Buffers

Direct3D has just the mechanism needed for this: vertex buffers. A vertex buffer, much like its name, is a memory store for vertices. The flexibility of vertex buffers makes them ideal for sharing transformed geometry in your scene. So how can the simple triangle application from Chapter 1, "Introducing Direct3D," be modified to use vertex buffers?

Creating a vertex buffer is quite simple. There are three constructors that can be used to do so; we will look at each one.

public VertexBuffer ( Microsoft.DirectX.Direct3D.Device device , System.Int32 
  sizeOfBufferInBytes , Microsoft.DirectX.Direct3D.Usage usage , 
  Microsoft.DirectX.Direct3D.VertexFormats vertexFormat , 
  Microsoft.DirectX.Direct3D.Pool pool )

public VertexBuffer ( System.Type typeVertexType , System.Int32 numVerts , 
  Microsoft.DirectX.Direct3D.Device device , 
  Microsoft.DirectX.Direct3D.Usage usage , 
  Microsoft.DirectX.Direct3D.VertexFormats vertexFormat , 
  Microsoft.DirectX.Direct3D.Pool pool )

The various parameter values can be

  • device—The Direct3D device you are using to create this vertex buffer. The vertex buffer will only be valid on this device.

  • sizeOfBufferInBytes—The size you want the vertex buffer to be, in bytes. If you are using the constructor with this parameter, the buffer will be able to hold any type of vertex.

  • typeVertexType—If you want your vertex buffer to only contain one type of vertex, you can specify that type here. This can be the type of one of the built-in vertex structures in the CustomVertex class, or it can be a user defined vertex type. This value cannot be null.

  • umVerts—When specifying the type of vertex you want to store in your buffer, you must also specify the maximum number of vertices the buffer will store of that type. This value must be greater than zero.

  • usage—Defines how this vertex buffer can be used. Not all members of the Usage type can be used when creating a vertex buffer. The following values are valid:

    • DoNotClip—Used to indicate that this vertex buffer will never require clipping. You must set the clipping render state to false when rendering from a vertex buffer using this flag.

    • Dynamic—Used to indicate that this vertex buffer requires dynamic memory use. If this flag isn't specified, the vertex buffer is static. Static vertex buffers are normally stored in video memory, while dynamic buffers are stored in AGP memory, so choosing this option is useful for drivers to determine where to store the memory. See the DirectX SDK documentation for more information on Dynamic usage.

    • Npatches—Used to indicate that this vertex buffer will be used to draw N-Patches.

    • Points—Used to indicate that this vertex buffer will be used to draw points.

    • RTPatches—Used to indicate that this vertex buffer will be used to draw high order primitives.

    • SoftwareProcessing—Used to indicate that vertex processing should happen in software. Otherwise, vertex processing should happen in hardware.

  • WriteOnly—Used to indicate that this vertex buffer will never be read from. Unless you have a very good reason for needing to read vertex buffer data, you should always select this option. Data stored in video memory that does not include this option suffers a severe performance penalty.

  • vertexFormat—Defines the format of the vertices that will be stored in this buffer. You can choose VertexFormat.None if you plan on having this be a generic buffer.

    • pool—Defines the memory pool where you want the vertex buffer to be located. You must specify one of the following memory pool locations:

    • Default—The vertex buffer is placed in the memory pool most appropriate for the data it contains. This is normally in either video memory or AGP memory, depending on the usage parameter. Vertex buffers created in this memory pool will be automatically disposed before a device reset.

    • Managed—The vertex buffer's data is automatically copied to device accessible memory as needed. The data is also backed by a system memory buffer, and can always be locked.

    • SystemMemory—The vertex buffer's data is placed in system memory, where it is not accessible from the device.

    • Scratch—A system memory pool that is not bound by a device, and thus cannot be used by the device. It is useful for manipulating data without being bound to a particular device format.

Creating Vertex Buffers from Unmanaged COM Pointers

Much like the device, there is an overload for our vertex buffer that takes an IntPtr. This value is used to pass in the actual COM interface pointer for the unmanaged IDirect3DVertexBuffer9 interface. This is useful when you want to use a vertex buffer created from an external (unmanaged) source. Any fully managed application will never use this constructor, and this value cannot be null.

In looking at the colored triangle application from Chapter 1, it should be relatively easy to move the triangle data into a vertex buffer. First, declare the vertex buffer member variable directly after our device:

private Device device = null;
private VertexBuffer vb = null;

Instead of creating the data every time the scene will be rendered, you will get greater performance by doing this only once. You can do this immediately after the device has been created, so move the triangle creation code there:

  // Create our device
  device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing
   , presentParams);

  CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[3];
  verts[0].SetPosition(new Vector3(0.0f, 1.0f, 1.0f));
  verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
  verts[1].SetPosition(new Vector3(-1.0f, -1.0f, 1.0f));
  verts[1].Color = System.Drawing.Color.Black.ToArgb();
  verts[2].SetPosition(new Vector3(1.0f, -1.0f, 1.0f));
  verts[2].Color = System.Drawing.Color.Purple.ToArgb();
  vb = new VertexBuffer(typeof(CustomVertex.PositionColored), 3, device, Usage.Dynamic |
      Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
  vb.SetData(verts, 0, LockFlags.None);

The only changes here are the two new lines after our triangle creation code. We first create a vertex buffer holding three members of the vertex structure type we've already declared. We want the buffer to be write-only, dynamic, and stored in the default memory pool for best performance. We then need to actually put our triangle list into our vertex buffer, and we accomplish this easily with the SetData method. This method accepts any generic object (much like DrawUserPrimitives) as its first member. The second member is the offset where we want to place our data, and since we want to fill in all of the data, we will use zero here. The last parameter is how we want the buffer locked while we are writing our data. We will discuss the various locking mechanisms shortly; for now, we don't care about how it's locked.

Compiling the application now will naturally give you a compile error since the DrawUserPrimitives call in OnPaint requires the "verts" variable. We need a way to tell Direct3D that we want to draw from our vertex buffer rather than the array we had declared before.

Naturally, this method does exist. We can call the SetStreamSource method from the device to have Direct3D read our vertex buffer when drawing our primitives. The prototypes for the two overloads of this function are

public void SetStreamSource ( System.Int32 streamNumber , 
  Microsoft.DirectX.Direct3D.VertexBuffer streamData , 
  System.Int32 offsetInBytes , System.Int32 stride )

public void SetStreamSource ( System.Int32 streamNumber , 
  Microsoft.DirectX.Direct3D.VertexBuffer streamData , 
  System.Int32 offsetInBytes )

The only difference between the two overloads is that one contains an extra member for the stride size of the stream. The first parameter is the stream number we will be using for this data. For now, we will always use zero as this parameter; however, we will discuss multiple streams in a later chapter. The second parameter is the vertex buffer that holds the data we want to use as our source. The third parameter is the offset (in bytes) into the vertex buffer that we want to be the beginning of the data Direct3D will draw from. The stride size parameter (which is only in one overload) is the size of each vertex in the buffer. If you've created your vertex buffer with a type, using the overload with this parameter isn't necessary.

Now replace the drawing call with the following code:

  device.SetStreamSource(0, vb, 0);
  device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);

As just described, we set stream zero to our vertex buffer, and since we want to use all of the data, we have our offset as zero as well. Notice that we've also changed the actual drawing call. Since we have all of our data in a vertex buffer, we no longer need to call the DrawUserPrimitives method because that function is only designed to draw user-defined data passed directly into the method. The more generic DrawPrimitives function will draw our primitives from our stream source. The DrawPrimitives method has three parameters, the first being the primitive type we've already discussed. The second parameter is the starting vertex in the stream. The last parameter is the number of primitives we will be drawing.

Even this simple demonstration of drawing a triangle with a vertex buffer shows approximately a 10% increase in performance based on frame rate. We will get into measuring performance and frame rates in later chapters. However, there is still a problem with this application that makes itself readily apparent when you attempt to resize your window. The triangle simply disappears as soon as your window is resized.

There are a few things going on here that are causing this behavior, and two of them have been mentioned briefly already. Remembering back to the previous chapter, we know that when our window is resized, our device is automatically reset. However, when a resource is created in the default memory pool (such as our vertex buffer), it is automatically disposed when the device is reset. So while our window is being resized, our device is being reset, and our vertex buffer disposed. One of the nifty features of Managed DirectX is that it will automatically re-create your vertex buffer for you after the device has been reset. However, there will be no data in the buffer, so the next time it's time to draw, nothing is shown.

The vertex buffer has an event we can capture, "created", that will inform us when it has been re-created and is ready to be filled with data. We should update our application to capture this event, and use that to fill our buffer with data. Add the following function to the application:

  private void OnVertexBufferCreate(object sender, EventArgs e)
  {
    VertexBuffer buffer = (VertexBuffer)sender;

    CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[3];
    verts[0].SetPosition(new Vector3(0.0f, 1.0f, 1.0f));
    verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
    verts[1].SetPosition(new Vector3(-1.0f, -1.0f, 1.0f));
    verts[1].Color = System.Drawing.Color.Black.ToArgb();
    verts[2].SetPosition(new Vector3(1.0f, -1.0f, 1.0f));
    verts[2].Color = System.Drawing.Color.Purple.ToArgb();

    buffer.SetData(verts, 0, LockFlags.None);
  }

This function has the standard event handler signature, taking in an object that is the item firing this event, plus the generic argument list. This event will never have an argument list, so you can safely ignore this member. We first get the vertex buffer that is firing this event by casting the sender member back to a vertex buffer. We then follow the exact same path we took last time by creating our data and calling SetData. We should replace the triangle creation code from our InitializeGraphics function now, and use the following two lines instead:

  vb.Created += new EventHandler(this.OnVertexBufferCreate);
  OnVertexBufferCreate(vb, null);

This code hooks the created event from the vertex buffer, and ensures that our OnVertexBufferCreate method is called whenever our vertex buffer has been created. Since the event hasn't been hooked up when the object is first created, we need to manually call our event handler function for the first time. Now run the application once more and try resizing the window.

You've now successfully updated the simple triangle application from an inefficient one to a much more efficient one using video memory and vertex buffers. However, it still seems quite boring, a single triangle spinning around. Now, you can try to make something even more exciting. A common first thing to try would be to render a box, so it's time to do that. The code will get slightly more complicated now, but all the basic principles will still apply.

Understanding Resource Lifetimes

All graphics resources will automatically be disposed if they are stored in video memory during a device reset; however, only vertex and index buffers will be re-created for you after the reset. Also note that all resources are also disposed when the device itself is disposed.

All geometry in a 3D scene is composed of triangles, so how would you render a box or cube? Well, to make a square you can use two triangles, and a box is composed of six squares. The only coordinates we really need are the eight corner vertices of the box, and we can create a box from those. Let's change our geometry creation code like in Listing 3.1:

Listing 3.1 Creating a Cube

CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[36];

// Front face
verts[0] = new CustomVertex.PositionColored(-1.0f, 1.0f, 1.0f, Color.Red.ToArgb());
verts[1] = new CustomVertex.PositionColored(-1.0f, -1.0f, 1.0f, Color.Red.ToArgb());
verts[2] = new CustomVertex.PositionColored(1.0f, 1.0f, 1.0f, Color.Red.ToArgb());
verts[3] = new CustomVertex.PositionColored(-1.0f, -1.0f, 1.0f, Color.Red.ToArgb());
verts[4] = new CustomVertex.PositionColored(1.0f, -1.0f, 1.0f, Color.Red.ToArgb());
verts[5] = new CustomVertex.PositionColored(1.0f, 1.0f, 1.0f, Color.Red.ToArgb());

// Back face (remember this is facing *away* from the camera, so vertices should be
  clockwise order)
verts[6] = new CustomVertex.PositionColored(-1.0f, 1.0f, -1.0f, Color.Blue.ToArgb());
verts[7] = new CustomVertex.PositionColored(1.0f, 1.0f, -1.0f, Color.Blue.ToArgb());
verts[8] = new CustomVertex.PositionColored(-1.0f, -1.0f, -1.0f, Color.Blue.ToArgb());
verts[9] = new CustomVertex.PositionColored(-1.0f, -1.0f, -1.0f, Color.Blue.ToArgb());
verts[10] = new CustomVertex.PositionColored(1.0f, 1.0f, -1.0f, Color.Blue.ToArgb());
verts[11] = new CustomVertex.PositionColored(1.0f, -1.0f, -1.0f, Color.Blue.ToArgb());

// Top face
verts[12] = new CustomVertex.PositionColored(-1.0f, 1.0f, 1.0f, Color.Yellow.ToArgb());
verts[13] = new CustomVertex.PositionColored(1.0f, 1.0f, -1.0f, Color.Yellow.ToArgb());
verts[14] = new CustomVertex.PositionColored(-1.0f, 1.0f, -1.0f, Color.Yellow.ToArgb());
verts[15] = new CustomVertex.PositionColored(-1.0f, 1.0f, 1.0f, Color.Yellow.ToArgb());
verts[16] = new CustomVertex.PositionColored(1.0f, 1.0f, 1.0f, Color.Yellow.ToArgb());
verts[17] = new CustomVertex.PositionColored(1.0f, 1.0f, -1.0f, Color.Yellow.ToArgb());

// Bottom face (remember this is facing *away* from the camera, so vertices should be 
  clockwise order)
verts[18] = new CustomVertex.PositionColored(-1.0f, -1.0f, 1.0f, Color.Black.ToArgb());
verts[19] = new CustomVertex.PositionColored(-1.0f, -1.0f, -1.0f, Color.Black.ToArgb());
verts[20] = new CustomVertex.PositionColored(1.0f, -1.0f, -1.0f, Color.Black.ToArgb());
verts[21] = new CustomVertex.PositionColored(-1.0f, -1.0f, 1.0f, Color.Black.ToArgb());
verts[22] = new CustomVertex.PositionColored(1.0f, -1.0f, -1.0f, Color.Black.ToArgb());
verts[23] = new CustomVertex.PositionColored(1.0f, -1.0f, 1.0f, Color.Black.ToArgb());

// Left face
verts[24] = new CustomVertex.PositionColored(-1.0f, 1.0f, 1.0f, Color.Gray.ToArgb());
verts[25] = new CustomVertex.PositionColored(-1.0f, -1.0f, -1.0f, Color.Gray.ToArgb());
verts[26] = new CustomVertex.PositionColored(-1.0f, -1.0f, 1.0f, Color.Gray.ToArgb());
verts[27] = new CustomVertex.PositionColored(-1.0f, 1.0f, -1.0f, Color.Gray.ToArgb());
verts[28] = new CustomVertex.PositionColored(-1.0f, -1.0f, -1.0f, Color.Gray.ToArgb());
verts[29] = new CustomVertex.PositionColored(-1.0f, 1.0f, 1.0f, Color.Gray.ToArgb());

// Right face (remember this is facing *away* from the camera, so vertices should be 
  clockwise order)
verts[30] = new CustomVertex.PositionColored(1.0f, 1.0f, 1.0f, Color.Green.ToArgb());
verts[31] = new CustomVertex.PositionColored(1.0f, -1.0f, 1.0f, Color.Green.ToArgb());
verts[32] = new CustomVertex.PositionColored(1.0f, -1.0f, -1.0f, Color.Green.ToArgb());
verts[33] = new CustomVertex.PositionColored(1.0f, 1.0f, -1.0f, Color.Green.ToArgb());
verts[34] = new CustomVertex.PositionColored(1.0f, 1.0f, 1.0f, Color.Green.ToArgb());
verts[35] = new CustomVertex.PositionColored(1.0f, -1.0f, -1.0f, Color.Green.ToArgb());

buffer.SetData(verts, 0, LockFlags.None);

Now that is a lot of vertices to have to type in: 36 to be exact, but don't worry. You can load the source code located on the included CD. As mentioned already, the box will be made of 12 triangles, and each triangle has 3 vertices, which gives the vertex list. Running the code just as it is will throw an exception during the SetData call. Can you guess why? If you guessed that we never updated the original size of our vertex buffer, you were correct. There are a couple extra changes we want to make before we're ready to really run this. Update the lines represented as follows:

vb = new VertexBuffer(typeof(CustomVertex.PositionColored), 36, device, Usage.Dynamic 
  | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);

device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI, angle / 
  (float)Math.PI * 2.0f, angle / (float)Math.PI);

device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);

The major thing we're doing is changing the size of the vertex buffer we've created to hold all the data we want to render. We've also changed the rotation somewhat to make the box spin a little crazier. Finally, we actually change our rendering call to render all 12 primitives, rather than the single triangle we had before. Actually, since our box is a fully formed 3D object that is completely filled, we no longer need to see the back-facing triangles. We can use the default culling mode in Direct3D (counterclockwise). Go ahead and remove the cull mode line from your source. You can try running the example now.

Terrific, we've now got a colorful box spinning around our screen. Each face of the box is a different color, and we can see each face of the box as it rotates without turning our back face culling off. If you wanted to render more than one box in a scene, hopefully you wouldn't create a series of vertex buffers, one for each box. There's a much easier way to do this.

We will draw a total of three boxes now, each side by side. Since our current camera settings have our first box taking up pretty much the entire scene, we should move our camera back a little first. Change the look at function as follows:

device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 18.0f), new Vector3(), 
  new Vector3(0,1,0));

As you can see, we just moved the position of our camera back so we can see more of the scene. Running the application now will show you the same box spinning around; it will just appear smaller, since the camera is farther away now. In order to draw two more boxes on the sides of this one, we can reuse our existing vertex buffer, and just tell Direct3D to draw the same vertices again. Add the following lines of code after the call to DrawPrimitives:

device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI, angle / 
  (float)Math.PI / 2.0f, angle / (float)Math.PI * 4.0f) * 
  Matrix.Translation(5.0f, 0.0f, 0.0f);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);

device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI, angle / 
  (float)Math.PI * 4.0f, angle / (float)Math.PI / 2.0f) * 
  Matrix.Translation(-5.0f, 0.0f, 0.0f);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);

So, what exactly are we doing here? Direct3D already knows what type of vertices we are planning on drawing, due to the VertexFormat property we set to draw our first box. It also knows what vertex buffer to retrieve the data from due to the SetStreamSource function also used on the first box. So what does Direct3D need to know in order to draw a second (and third) box? The only information that is needed is where and what to draw.

Setting the world transform will "move" our data from object space into world space, so what are we using as our transformation matrix? First, we rotate much like we did in the SetupCamera function; although we use a slightly different rotation function, that's just so the boxes rotate at different angles. The second half of the world transform is new, though. We multiply a Matrix.Translation to our existing rotation matrix. A translation matrix provides a way to move vertices from one point to another in world space. Looking at our translation points, we want to move the second box five units to the right, while we are moving the third box five units to the left.

You will also notice that we multiply our two transformation matrices, which provides us with a cumulative transformation of the arguments. They are done in the order they are specified, so in this case, our vertices will be rotated first, then translated (moved) second. Translating and then rotating would provide very different results. It is important to remember the order of operations when you are transforming vertices.

The code included with the CD shows updated code that draws a total of nine boxes rather than the three shown here. See Figure 3.1.

Figure 3.1Figure 3.1 Colored cubes.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020