William J. Buchanan BSc, CEng, PhD, in Software Development for Engineers, 1997 A constructor allows for the initialization of a class and a destructor allows the class function to be removed from memory. They are defined as follows: A constructor is a special initialization function that is automatically called whenever a class is declared. The constructor always has the same name as the class name, and no data types are defined for the argument list or the return type. Normally a constructor is used to initialize a class. A destructor is a member of a function which is automatically called when the class is destroyed. It has the same
name as the class name but is preceded by a tilde (~). Normally a destructor is used to clean-up when the class is destroyed. C++ Program 12.1 has a class which is named class_ex. The constructor for this class is class_ex() and the destructor is ~class_ex(). Test run 12.1 shows a sample run. It can be seen that initially when the program is run the message Constructing is displayed. This is because the
class is initially declared to c1. Then when the function test() is called the Constructing message is again displayed as a new class is defined (c2). When the program leaves this function the destructor is called and thus the message Destructor is displayed. Finally, when the program quits the destructor is again called. Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780340700143500578 Light SourcesMatt Pharr, ... Greg Humphreys, in Physically Based Rendering (Third Edition), 2017 12.6 Infinite area lightsAnother useful kind of light is the infinite area light—an infinitely far away area light source that surrounds the entire scene. One way to visualize this light is as an enormous sphere that casts light into the scene from every direction. One important use of infinite area lights is for environment lighting, where an image that represents illumination in an environment is used to illuminate synthetic objects as if they were in that environment. Figures 12.19 and 12.20 compare illuminating a car model with a standard area light to illuminating it with environment maps that simulate illumination from the sky at a few different times of day (the illumination maps used are shown in Figure 12.21). The increase in realism is striking. The InfiniteAreaLight class is implemented in lights/infinite.h and lights/infinite.cpp. Figure 12.19. Car model (a) illuminated with an area light and a directional light and (b) illuminated with morning skylight from an environment map. Using a realistic distribution of illumination gives an image that is much more visually compelling. In particular, with illumination arriving from all directions, the glossy reflective properties of the paint are much more visually apparent. (Model courtesy of Yasutoshi Mori.)Figure 12.20. Changing just the environment map used for illumination gives quite different results in the final image: (a) using a midday skylight distribution and (b) using a sunset environment map. (Model courtesy of Yasutoshi Mori.)Figure 12.21. Environment Maps Used for Illumination in Figures 12.19 and 12.20. (a) Morning, (b) midday, and (c) sunset sky. (The bottom halves of these maps aren’t shown here, since they are just black pixels.) A widely used representation for light for this application is the latitude–longitude radiance map. (This representation is also known as the equirectangular projection.) The EnvironmentCamera can be used to create image maps for the light, or see the “Further Reading” section for information about techniques for capturing this lighting data from real-world environments. 〈InfiniteAreaLight Declarations〉 ≡ class InfiniteAreaLight : public Light { public: 〈InfiniteAreaLight Public Methods 740〉 private: 〈InfiniteAreaLight Private Data 740〉 }; EnvironmentCamera 376 InfiniteAreaLight 737 Light 714 Like the other lights, the InfiniteAreaLight takes a transformation matrix; here, its use is to orient the image map. It then uses spherical coordinates to map from directions on the sphere to (θ, ϕ) directions, and from there to (u, v) texture coordinates. The provided transformation thus determines which direction is “up.” The constructor loads the image data from the disk and creates a MIPMap to store it. The fragment that loads the data, 〈Read texel data from texmap and initialize Lmap〉, is straightforward and won’t be included here. The other code fragment in the constructor, 〈Initialize sampling PDFs for infinite area light〉, is related to Monte Carlo sampling of InfiniteAreaLights and will be defined later, in Section 14.2.4. InfiniteAreaLight 737 MIPMap 625 As with DistantLights, because the light is defined as being infinitely far away, the MediumInterface for an infinite area light must have nullptr values for its Medium *s, corresponding to a vacuum. DistantLight 731 InfiniteAreaLight 737 Light 714 LightFlags::Infinite 715 MediumInterface 684 Spectrum 315 Transform 83 〈InfiniteAreaLight Method Definitions〉 ≡ InfiniteAreaLight::InfiniteAreaLight(const Transform &LightToWorld, const Spectrum &L, int nSamples, const std::string &texmap) : Light((int)LightFlags::Infinite, LightToWorld, MediumInterface(), nSamples) { 〈Read texel data from texmap and initialize Lmap〉 〈Initialize sampling PDFs for infinite area light 847〉 } 〈InfiniteAreaLight Private Data〉 ≡ 737 std::unique_ptr < MIPMap < RGBSpectrum > > Lmap; Like DistantLights, InfiniteAreaLights also need the scene bounds; here again, the Preprocess() method finds the scene bounds after all of the scene geometry has been created. 〈InfiniteAreaLight Public Methods〉 ≡ 737 void Preprocess(const Scene &scene) { scene.WorldBound().BoundingSphere(&worldCenter, &worldRadius); } 〈InfiniteAreaLight Private Data〉 + ≡ 737 Point3f worldCenter; Float worldRadius; Bounds3::BoundingSphere() 81 DistantLight 731 Float 1062 InfiniteAreaLight::worldCenter 740 InfiniteAreaLight::worldRadius 740 MIPMap 625 Point3f 68 RGBSpectrum 332 Scene 23 Scene::WorldBound() 24 Because InfiniteAreaLights cast light from all directions, it’s also necessary to use Monte Carlo integration to sample their illumination. Therefore, the InfiniteAreaLight::Sample_Li() method will be defined in Section 14.2. Like directional lights, the total power from the infinite area light is related to the surface area of the scene. Like many other lights in this chapter, the power computed here is approximate; here, all texels are given equal weight, which ignores the fact that with an equirectangular projection, the differential solid angle subtended by each pixel values with its θ value (Section 14.2.4). 〈InfiniteAreaLight Method Definitions〉 + ≡ Spectrum InfiniteAreaLight::Power() const { return Pi * worldRadius * worldRadius * Spectrum(Lmap- > Lookup(Point2f(.5f, .5f), .5f), SpectrumType::Illuminant); } Because infinite area lights need to be able to contribute radiance to rays that don’t hit any geometry in the scene, we’ll add a method to the base Light class that returns emitted radiance due to that light along a ray that escapes the scene bounds. (The default implementation for other lights returns no radiance.) It is the responsibility of the integrators to call this method for these rays. 〈Light Method Definitions〉 + ≡ Spectrum Light::Le(const RayDifferential &ray) const { return Spectrum(0.f); } InfiniteAreaLight 737 InfiniteAreaLight::Lmap 740 InfiniteAreaLight::Sample_Li() 849 InfiniteAreaLight::worldRadius 740 Inv2Pi 1063 InvPi 1063 Light 714 Light::WorldToLight 715 MIPMap::Lookup() 635 Pi 1063 Point2f 68 Ray::d 73 RayDifferential 75 Spectrum 315 SpectrumType::Illuminant 330 SphericalPhi() 346 SphericalTheta() 346 Vector3::Normalize() 66 Vector3f 60 〈InfiniteAreaLight Method Definitions〉 + ≡ Spectrum InfiniteAreaLight::Le(const RayDifferential &ray) const { Vector3f w = Normalize(WorldToLight(ray.d)); Point2f st(SphericalPhi(w) * Inv2Pi, SphericalTheta(w) * InvPi); return Spectrum(Lmap- > Lookup(st), SpectrumType::Illuminant); } Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780128006450500129 Data Abstraction and Object OrientationMichael L. Scott, in Programming Language Pragmatics (Third Edition), 2009 9.3.1 Choosing a ConstructorExample 9.22 Naming Constructors in EiffelSmalltalk, Eiffel, C++, Java, and C# all allow the programmar to specify more than one constructor for a given class. In C++, Java, and C#, the constructors behave like overloaded subroutines: they must be distinguished by their numbers and types of arguments. In Smalltalk and Eiffel, different constructors can have different names; code that creates an object must name a constructor explicitly. In Eiffel one might say class COMPLEX creation new_cartesian, new_polar feature {ANY} x, y : REAL new_cartesian(x_val, y_val : REAL) is do x := x_val; y := y_val end new_polar(rho, theta : REAL) is do x := rho * cos(theta) y := rho * sin(theta) end -- other public methods feature {NONE} -- private methods end -- class COMPLEX … a, b : COMPLEX … !!b.new_cartesian(0, 1) !!a.new_polar(pi/2, 1) The !! operator is Eiffel's equivalent of new. Because class COMPLEX specified constructor (“creator”) methods, the compiler will insist that every use of !! specify a constructor name and arguments. There is no straightforward analog of this code in C++; the fact that both constructors take two real arguments means that they could not be distinguished by overloading. Smalltalk resembles Eiffel in the use of multiple named constructors, but it distinguishes more sharply between operations that pertain to an individual object and operations that pertain to a class of objects. Smalltalk also adopts an anthropomorphic programming model in which every operation is seen as being executed by some specific object in response to a request (a “message”) from some other object. Since it makes little sense for an object O to create itself, O must be created by some other object (call it C) that represents O's class. Of course, because C is an object, it must itself belong to some class. The result of this reasoning is a system in which each class definition really introduces a pair of classes and a pair of objects to represent them. Objective-C and CLOS have similar dual hierarchies. Example 9.23 Metaclasses in SmalltalkConsider, for example, the standard class named Date. Corresponding to Date is a single object (call it D) that performs operations on behalf of the class. In particular, it is D that creates new objects of class Date. Because only objects execute operations (classes don't), we don't really need a name for D; we can simply use the name of the class it represents: todaysDate <- Date today This code causes D to execute the today constructor of class Date, and assigns a reference to the newly created object into a variable named todaysDate. So what is the class of D ? It clearly isn't Date, because D represents class Date. Smalltalk says that D is an object (in fact the only object) of the metaclass Date class. For technical reasons, it is also necessary for Date class to be represented by an object. To avoid an infinite regression, all objects that represent metaclasses are instances of a single class named Metaclass. Modula-3 and Oberon provide no constructors at all: the programmar must initialize everything explicitly. Ada 95 supports constructors and destructors (called Initialize and Finalize routines) only for objects of types derived from the standard library type Controlled. Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780123745149000197 Functional Modeling with OCL ContractsRaul Sidnei Wazlawick, in Object-Oriented Analysis and Design for Information Systems, 2014 8.4 Query returnAs mentioned before, system commands change data while queries just return data to the user. The contracts for system queries must define what is returned, and this can be done in OCL by using the body clause. Expressions that represent preconditions are all Boolean, but expressions that represent the return of a query may have other types. They can return strings, numbers, lists, tuples, or even more complex structures. The following examples are based on the model of Figure 8.1. Initially, a query is defined that returns the total of a given cart: Context Livir::getCartTotal(aCartId:CartId):Money body: cart[aCartId].total System queries as well as system commands always have the Controller as their context. Therefore, in the example above, cart is a property of the controller Livir: an association role from it to the class Cart. The following query returns the name and birthdate of a given customer: Context Livir::getCustomerNameBirthDate(aCustomerId:CustomerId):Tuple body: Tuple { name=customer[aCustomerId].name, birthDate=customer[aCustomerId].birthDate } The Tuple constructor is one of the ways to represent DTOs4 in OCL; the tuple works as a Pascal record in the example with two fields: name and birthDate. The values of the fields are defined by the expressions after the “=”. To avoid repeating expressions like customer[aCustomerId] or even more complex expressions, the def clause can be used in order to define a constant that refers to the original expression. Using the def clause, the contract above would look like the following: Context Livir::getCustomerNameBirthDate(aCustomerId:CustomerId):Tuple def: aCustomer=customer[aCustomerId] body: Tuple { name=aCustomer.name, birthDate=aCustomer.birthDate } The expression inside the def clause indicates that the identifier aCustomer is referring to the customer whose ID is the parameter aCustomerId received from the query. The following expression makes a projection in the set of customers returning the names of all customers: Context Livir::listCustomerNames():Set body: customer.name The next expression applies a filter and a projection, returning the names of all customers that are less than 25 years old: Context Livir::listYoungCustomers():Set body: customer->select(birthDate.addYear(25)>Date.getCurrent()).name The idea in the example above is to add 25 years to the birthdate of the customer and compare it with the current date: if the customer reaches her 25th birthday after the current date then she is younger than 25 years. OCL also has operations to add days and months to dates: addDay and addMonth, respectively. The expression Date.currentDate returns the system’s current date. The last example is a query that returns the cart summary, as shown in Figure 8.2. Context Livir::getCartSummary(aCartId:CartId):Tuple def: aCart=cart[aCartId] body: Tuple { total=aCart.total, items=aCart.item->collect(anItem| Tuple { title=anItem.book.title, authorsName=anItem.book.authorsName, quantity=anItem.quantity, unitPrice=anItem.unitPrice, subtotal=anItem.subtotal } ) } The collect expression is a way to obtain a set whose elements are properties or transformations on properties of another set. The dot notation (“.”) itself is an abbreviated form of collect. For example, customer.name is equivalent to customer->collect(aCustomer|aCustomer.name) or customer->collect(name). When possible, the dot notation is preferred because it is shorter. But in the getCartSummary example, the need to create a tuple instead of accessing a property of the elements of a set prevents the use of the dot, because the Tuple operator is prefixed. Thus, the collect expression has to be explicitly used in that case. In the example, two tuple structures were built: the outer one contains the total of the cart and the ordered set of its items; the inner one contains for each item the information that is needed as mentioned in Figure 8.2: book title, author’s name, etc. Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780124186736000089 Scripting LanguagesMichael L. Scott, in Programming Language Pragmatics (Third Edition), 2009 Python and RubyAs we have noted, both Python and Ruby are explicitly object-oriented. Both employ a uniform reference model for variables. Like Smalltalk, both incorporate an object hierarchy in which classes themselves are represented by objects. The root class in Python is called object; in Ruby it is Object. Example 13.81 Constructors in Python and RubyIn both Python and Ruby, each class has a single distinguished constructor, which cannot be overloaded. In Python it is __init__; in Ruby it is initialize. To create a new object in Python one says my_object = My_class(args); in Ruby one says my_object = My_class.new(args). In each case the args are passed to the constructor. To achieve the effect of overloading, with different numbers or types of arguments, one must arrange for the single constructor to inspect its arguments explicitly. We employed a similar idiom in Perl (in the new routine of Figure 13.21) and JavaScript (in the Integer function of Figure 13.22). Both Python and Ruby are more flexible than PHP or more traditional object-oriented languages regarding the contents (members) of a class. New fields can be added to a Python object simply by assigning to them: my_object.new_field = value. The set of methods, however, is fixed when the class is first defined. In Ruby only methods are visible outside a class (“put” and “get” methods must be used to access fields), and all methods must be explicitly declared. It is possible, however, to modify an existing class declaration, adding or overriding methods. One can even do this on an object-by-object basis. As a result, two objects of the same class may not display the same behavior. Example 13.82 Naming Class Members in Python and RubyPython and Ruby differ in many other ways. The initial parameter to methods is explicit in Python; by convention it is usually named self. In Ruby self is a keyword, and the parameter it represents is invisible. Any variable beginning with a single @ sign in Ruby is a field of the current object. Within a Python method, uses of object members must name the object explicitly. One must, for example, write self.print(); just print() will not suffice. Design & ImplementationExecutable class declarationsBoth Python and Ruby take the interesting position that class declarations are executable code. Elaboration of a declaration executes the code inside. Among other things, we can use this mechanism to achieve the effect of conditional compilation: class My_class # Ruby code def initialize(a, b) @a = a; @b = b; end if expensive_function() def get() return @a end else def get() return @b end end end Instead of computing the expensive function inside get, on every invocation, we compute it once, ahead of time, and define an appropriate specialized version of get. Ruby methods may be public, protected, or private.11 Access control in Python is purely a matter of convention; both methods and fields are universally accessible. Finally, Python has multiple inheritance. Ruby has mix-in inheritance: a class cannot obtain data from more than one ancestor. Unlike most other languages, however, Ruby allows an interface (mix-in) to define not only the signatures of methods, but also their implementation (code). Check Your Understanding44.Contrast the philosophies of Perl and Ruby with regard to error checking and reporting. 45.Compare the numeric types of popular scripting languages to those of compiled languages like C or Fortran. 46.What are bignums? Which languages support them? 47.What are associative arrays? By what other names are they sometimes known? 48.Why don't most scripting languages provide direct support for records? 49.What is a typeglob in Perl? What purpose does it serve? 50.Describe the tuple and set types of Python. 51.Explain the unification of arrays and hashes in PHP and Tcl. 52.Explain the unification of arrays and objects in JavaScript. 53.Explain how tuples and hashes can be used to emulate multidimensional arrays in Python. 54.Explain the concept of context in Perl. How is it related to type compatibility and type inference? What are the two principal contexts defined by the language's operators? 55.Compare the approaches to object orientation taken by Perl 5, PHP 5, JavaScript, Python, and Ruby. 56.What is meant by the blessing of a reference in Perl? 57.What are prototypes in JavaScript? What purpose do they serve? Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780123745149000239 OpenMPVictor Alessandrini, in Shared Memory Application Programming, 2016 Constructing the directed acyclic graphFigure 10.10 shows the construction of the directed acyclic graph, which proceeds as follows: Figure 10.10. Constructing a directed acyclic graph. • The constructor Graph G creates a graph. It defines internally an empty STL vector of cells. •The member function G.create_random_dag(N) starts by allocating an STL vector of cells of size N. Then, successive vector elements are visited. For each vector element, the number of ancestors is randomly selected. If there are ancestors, they are again randomly chosen among the preceding vector elements. Then, the cell is initialized (ref_count, input pointers) and, in the ancestor cells, a pointer to the current cell is added to the successors vector. At this point, the graph is constructed. It follows from this procedure that the number of ancestors is limited to 2, but the number of successors is arbitrary. •G.get_root_set(std::vector<Cell*> root_set) is another useful function. It receives as argument a reference to an empty STL vector of cell pointers. It fills this vector with the addresses of the root cells, namely, cells that have no ancestors. Obviously, cells with no ancestors are the starting point of the graph traversal algorithm. Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780128037614000101 C++ UsageMartin Reddy, in API Design for C++, 2011 6.2.2 Defining Constructors and AssignmentBecause writing constructors and operators can be a tricky business, here's an example that demonstrates the various combinations. It builds on the previous array example and presents a class for storing an array of strings. Because the array is allocated dynamically, you must define a copy constructor and assignment operator, otherwise the memory will be freed twice on destruction if you copy the array. Here's the declaration of the Array class in the header file: #include <string> class Array { public: // default constructor Array(); // non-default constructor explicit Array(int size); // destructor ~Array(); // copy constructor Array(const Array &in_array); // assignment operator Array &operator = (const Array &in_array); std::string Get(int index) const; bool Set(int index, const std::string &str); int GetSize() const; private: int mSize; std::string *mArray; }; and here are sample definitions for the constructors and assignment operator: #include "array.h" #include <algorithm> // default constructor Array::Array() : mSize(0), mArray(NULL) { } // non-default constructor Array::Array(int size) : mSize(size), mArray(new std::string[size]) { } // destructor Array::~Array() { delete [] mArray; } // copy constructor Array::Array(const Array &in_array) : mSize(in_array.mSize), mArray(new std::string[in_array.mSize]) { std::copy(in_array.mArray, in_array.mArray + mSize, mArray); } // assignment operator Array &Array::operator = (const Array &in_array) { if (this != &in_array) // check for self assignment { delete [] mArray; // delete current array first mSize = in_array.mSize; mArray = new std::string[in_array.mSize]; std::copy(in_array.mArray, in_array.mArray + mSize, mArray); } return *this; } Given the aforementioned Array class, the following code demonstrates when the various methods will be called. Array a; // default constructor Array a(10); // non-default constructor Array b(a); // copy constructor Array c = a; // copy constructor (because c does not exist yet) b = c; // assignment operator Note that there are certain cases where your compiler may elide the call to your copy constructor, for example, if it performs some form of Return Value Optimization (Meyers, 1998). Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780123850034000063 Object-Oriented ProgrammingRaymond Greenlaw, Y. Daniel Liang, in Encyclopedia of Information Systems, 2003 II.A. Creating Objects and Object Reference VariablesObjects are created through constructors. A constructor has exactly the same name as its defining class. Constructors are a special kind of method. Like methods, constructors can be overloaded, making it easier to develop objects with different initial data values. Constructors play the role of initializing objects. A constructor with no parameters is referred to as a default constructor. If a class does not define any constructors explicitly (the case in Listing 1), a default constructor is assumed implicitly. If a class defines constructors explicitly, a default constructor will not exist unless it is defined explicitly. Listing 2 expands the Person class defined in Listing 1 with two constructors. Listing 2: Class definition for Person with constructors. Objects are created using the new operator on a constructor as follows: For example, new Person () creates an object of the Person class. Newly created objects are allocated in memory. Objects are accessed via object reference variables to the objects. Such reference variables are declared using the following syntax: The types of reference variables are known as reference types. The following statement declares the variable p to be of the Person type: The variable p can reference a Person object. The following statement creates an object and assigns its reference to p. You can combine the declaration of an object reference variable, creation of an object, and assignment of the object reference to the variable together in one statement using the following syntax: Below is a concrete example of this syntax: The variable p now holds a reference to a Person object. When a variable of a reference type is declared, the variable holds a special value, null, which means that the variable does not reference any object. Once an object is created, its reference can be assigned to a variable. For example, the statement creates a Person object by allocating the memory space for the object and assigns its memory reference to the variable p. When you assign one variable into another, the other variable is set to the same value. After an object is created, its data can be accessed and its methods can be invoked using the following dot notation: For example, p.setName (“Sam J Fox”) invokes the setName method of object p. Methods are invoked as operations on objects. If the address property were defined as public, the variable address could be accessed using p.address. Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B0122272404001246 MaterialsMatt Pharr, ... Greg Humphreys, in Physically Based Rendering (Third Edition), 2017 9.1 BSDFsThe BSDF class represents a collection of BRDFs and BTDFs. Grouping them in this manner allows the rest of the system to work with composite BSDFs directly, rather than having to consider all of the components they may have been built from. Equally important, the BSDF class hides some of the details of shading normals from the rest of the system. Shading normals, either from per-vertex normals in triangle meshes or from bump mapping, can substantially improve the visual richness of rendered scenes, but because they are an ad hoc construct, they are tricky to incorporate into a physically based renderer. The issues that they introduce are handled in the BSDF implementation. 〈BSDF Declarations〉 + ≡ class BSDF { public: 〈BSDF Public Methods 573〉 〈BSDF Public Data 573〉 private: 〈BSDF Private Methods 576〉 〈BSDF Private Data 573〉 }; The BSDF constructor takes a SurfaceInteraction object that contains information about the differential geometry at the point on a surface as well as a parameter eta that gives the relative index of refraction over the boundary. For opaque surfaces, eta isn’t used, and a value of one should be provided by the caller. (The default value of one for eta is for just this case.) The constructor computes an orthonormal coordinate system with the shading normal as one of the axes; this coordinate system will be useful for transforming directions to and from the BxDF coordinate system that is described in Figure 8.2. Throughout this section, we will use the convention that ns denotes the shading normal and ng the geometric normal (Figure 9.1). Figure 9.1. The geometric normal, ng, defined by the surface geometry, and the shading normal, ns, given by per-vertex normals and/or bump mapping, will generally define different hemispheres for integrating incident illumination to compute surface reflection. This inconsistency is important to handle carefully since it can otherwise lead to artifacts in images. BSDF 572 BxDF 513 SurfaceInteraction 116 〈BSDF Public Methods〉 ≡ 572 BSDF(const SurfaceInteraction &si, Float eta = 1) : eta(eta), ns(si.shading.n), ng(si.n), ss(Normalize(si.shading.dpdu)), ts(Cross(ns, ss)) { } 〈BSDF Public Data〉 ≡ 572 const Float eta; 〈BSDF Private Data〉 ≡ 572 const Normal3f ns, ng; const Vector3f ss, ts; The BSDF implementation stores only a limited number of individual BxDF components. It could easily be extended to allocate more space if more components were given to it, although this isn’t necessary for any of the Material implementations in pbrt thus far, and the current limit of eight is plenty for almost all practical applications. 〈BSDF Public Methods〉 + ≡ 572 void Add(BxDF *b) { Assert(nBxDFs < MaxBxDFs); bxdfs[nBxDFs++] = b; } 〈BSDF Private Data〉 + ≡ 572 int nBxDFs = 0; static constexpr int MaxBxDFs = 8; BxDF *bxdfs[MaxBxDFs]; For other parts of the system that need additional information about the particular BRDFs and BTDFs that are present, a method returns the number of BxDFs stored by the BSDF that match a particular set of BxDFType flags. 〈BSDF Public Methods〉 + ≡ 572 int NumComponents(BxDFType flags = BSDF_ALL) const; The BSDF also has methods that perform transformations to and from the local coordinate system used by BxDFs. Recall that, in this coordinate system, the surface normal is along the z axis (0, 0, 1), the primary tangent is (1, 0, 0), and the secondary tangent is (0, 1, 0). The transformation of directions into “shading space” simplifies many of the BxDF implementations in Chapter 8. Given three orthonormal vectors s, t, and n in world space, the matrix M that transforms vectors in world space to the local reflection space is M=sxsy sztxtytznxnynz =stn . Assert() 1069 BSDF 572 BSDF::bxdfs 573 BSDF::nBxDFs 573 BSDF_ALL 513 BxDF 513 BxDFType 513 Cross() 65 Float 1062 Material 577 Normal3f 71 SurfaceInteraction 116 Vector3::Normalize() 66 Vector3f 60 To confirm this yourself, consider, for example, the value of M times the surface normal n, Mn = (s • n, t • n, n • n). Since s, t, and n are all orthonormal, the x and y components of Mn are zero. Since n is normalized, n • n = 1. Thus, Mn = (0, 0, 1), as expected. In this case, we don’t need to compute the inverse transpose of M to transform normals (recall the discussion of transforming normals in Section 2.8.3). Because M is an orthogonal matrix (its rows and columns are mutually orthogonal), its inverse is equal to its transpose, so it is its own inverse transpose already. 〈BSDF Public Methods〉 + ≡ 572 Vector3f WorldToLocal(const Vector3f &v) const { return Vector3f(Dot(v, ss), Dot(v, ts), Dot(v, ns)); } The method that takes vectors back from local space to world space transposes M to find its inverse before doing the appropriate dot products. 〈BSDF Public Methods〉 + ≡ 572 Vector3f LocalToWorld(const Vector3f &v) const { return Vector3f(ss.x * v.x + ts.x * v.y + ns.x * v.z, ss.y * v.x + ts.y * v.y + ns.y * v.z, ss.z * v.x + ts.z * v.y + ns.z * v.z); } Shading normals can cause a variety of undesirable artifacts in practice (Figure 9.2). Figure 9.2(a) shows a light leak: the geometric normal indicates that ωi and ωo lie on opposite sides of the surface, so if the surface is not transmissive, the light should have no contribution. However, if we directly evaluate the scattering equation, Equation (5.9), about the hemisphere centered around the shading normal, we will incorrectly incorporate the light from ωi. This case demonstrates that ns can’t just be used as a direct replacement for ng in rendering computations. Figure 9.2. The Two Types of Errors That Result from Using Shading Normals. (a) A light leak: the geometric normal indicates that the light is on the back side of the surface, but the shading normal indicates the light is visible (assuming a reflective and not transmissive surface). (b) A dark spot: the geometric normal indicates that the surface is illuminated, but the shading normal indicates that the viewer is behind the lit side of the surface. BSDF::ns 573 BSDF::ss 573 BSDF::ts 573 Dot() 63 Vector3f 60 Figure 9.2(b) shows a similar tricky situation: the shading normal indicates that no light should be reflected to the viewer, since it is not in the same hemisphere as the illumination, while the geometric normal indicates that they are in the same hemisphere. Direct use of ns would cause ugly black spots on the surface where this situation happens. Fortunately, there is an elegant solution to these problems. When evaluating the BSDF, we can use the geometric normal to decide if we should be evaluating reflection or transmission: if ωi and ωo lie in the same hemisphere with respect to ng, we evaluate the BRDFs, and otherwise we evaluate the BTDFs. In evaluating the scattering equation, however, the dot product of the normal and the incident direction is still taken with the shading normal rather than the geometric normal. Now it should be clear why pbrt requires BxDFs to evaluate their values without regard to whether ωi and ωo are in the same or different hemispheres. This convention means that light leaks are avoided, since we will only evaluate the BTDFs for the situation in Figure 9.2(a), giving no reflection for a purely reflective surface. Similarly, black spots are avoided since we will evaluate the BRDFs for the situation in Figure 9.2(b), even though the shading normal would suggest that the directions are in different hemispheres. Given these conventions, the method that evaluates the BSDF for a given pair of directions follows directly. It starts by transforming the world space direction vectors to local BSDF space and then determines whether it should use the BRDFs or the BTDFs. It then loops over the appropriate set and evaluates the sum of their contributions. 〈BSDF Method Definitions〉 ≡ Spectrum BSDF::f(const Vector3f &woW, const Vector3f &wiW, BxDFType flags) const { Vector3f wi = WorldToLocal(wiW), wo = WorldToLocal(woW); bool reflect = Dot(wiW, ng) * Dot(woW, ng) > 0; Spectrum f(0.f); for (int i = 0; i < nBxDFs; ++i) if (bxdfs[i]- > MatchesFlags(flags) && ((reflect && (bxdfs[i]- > type & BSDF_REFLECTION)) ∥ (!reflect && (bxdfs[i]- > type & BSDF_TRANSMISSION)))) f + = bxdfs[i]- > f(wo, wi); return f; } pbrt also provides BSDF methods that return the BSDF’s reflectances. (Recall the definition of reflectance in Section 8.1.1.) The two corresponding methods just loop over the BxDFs and sum the values returned by their BxDF::rho() methods; their straightforward implementations aren’t included here. These methods take arrays of samples for BxDFs for use in Monte Carlo sampling algorithms if needed (recall the BxDF::rho() interface defined in Section 8.1.1, which takes such samples as well.) BSDF 572 BSDF::bxdfs 573 BSDF::nBxDFs 573 BSDF::WorldToLocal() 574 BSDF_ALL 513 BSDF_REFLECTION 513 BSDF_TRANSMISSION 513 BxDF 513 BxDF::f() 514 BxDF::MatchesFlags() 513 BxDF::rho() 515 BxDFType 513 Dot() 63 Point2f 68 Spectrum 315 Vector3f 60 〈BSDF Public Methods〉 + ≡ 572 Spectrum rho(int nSamples, const Point2f *samples1, const Point2f *samples2, BxDFType flags = BSDF_ALL) const; Spectrum rho(const Vector3f &wo, int nSamples, const Point2f *samples, BxDFType flags = BSDF_ALL) const; 9.1.1 BSDF Memory managementFor each ray that intersects geometry in the scene, one or more BSDF objects will be created by the Integrator in the process of computing the radiance carried along the ray. (Integrators that account for multiple interreflections of light will generally create a number of BSDFs along the way.) Each of these BSDFs in turn has a number of BxDFs stored inside it, as created by the Materials at the intersection points. A naïve implementation would use new and delete to dynamically allocate storage for both the BSDF as well as each of the BxDFs that it holds. Unfortunately, such an approach would be unacceptably inefficient—too much time would be spent in the dynamic memory management routines for a series of small memory allocations. Instead, the implementation here uses a specialized allocation scheme based on the MemoryArena class described in Section A.4.3.1 A MemoryArena is passed into methods that allocate memory for BSDFs. For example, the SamplerIntegrator::Render() method creates a MemoryArena for each image tile and passes it to the integrators, which in turn pass it to the Material. For the convenience of code that allocates BSDFs and BxDFs (e.g., the Materials in this chapter), there is a macro that hides some of the messiness of using the memory arena. Instead of using the new operator to allocate those objects like this: BSDF *b = new BSDF; BxDF *lam = new LambertianReflection(Spectrum(0.5f)); code should instead be written with the ARENA_ALLOC() macro, like this: BSDF *b = ARENA_ALLOC(arena, BSDF); BxDF *lam = ARENA_ALLOC(arena, LambertianReflection)(Spectrum(0.5f)); where arena is a MemoryArena. The ARENA_ALLOC() macro uses the placement operator new to run the constructor for the object at the returned memory location. 〈Memory Declarations〉 ≡ #define ARENA_ALLOC(arena, Type) new (arena.Alloc(sizeof(Type))) Type The BSDF destructor is a private method in order to ensure that it isn’t inadvertently called (e.g., due to an attempt to delete a BSDF). Making the destructor private ensures a compile time error if it is called. Trying to delete memory allocated by the MemoryArena could lead to errors or crashes, since a pointer to the middle of memory managed by the MemoryArena would be passed to the system’s dynamic memory freeing routine. In turn, an implication of the allocation scheme here is that BSDF and BxDF destructors are never executed. This isn’t a problem for the ones currently implemented in the system. 〈BSDF Private Methods〉 ≡ 572 ~ BSDF() { } ARENA_ALLOC() 576 BSDF 572 BxDF 513 Integrator 25 Material 577 MemoryArena 1074 MemoryArena::Alloc() 1074 MemoryArena::Reset() 1076 SamplerIntegrator::Render() 26 Read full chapter URL: https://www.sciencedirect.com/science/article/pii/B9780128006450500099 Java SocketsJames C. Foster, Mike Price, in Sockets, Shellcode, Porting, & Coding, 2005 TCP clientsTCP client socket programming is simple using the java.net package. A single class (Socket) is used to create and manage the details of new TCP connections. Data is transferred to and from the socket using the standard InputStream and OutputStream classes located in the java, io package. The Socket class provides several constructors and methods useful for establishment, control, and termination of TCP connections. The constructors are used to define and establish new connections. The remaining methods are used to send and receive data, retrieve information on established connections, fine-tune various aspects of data transfer, determine connection state, and for connection termination. Of these constructors and methods, only a few are required to implement basic TCP client socket functionality (see Example 5.1). TCP Client Socket (TCPClientl.java) 1 /* 2 * TCPClient1.java 3 * 4 * TCP client socket program to connect, request 5 * and receive data using TCP and HTTP 1.0 6 * protocols. 7 * 8 * Usage : 9 * 10 * java TCPClientl <target_ip> <target_port> <resource> 11 * 12 * 13 */ 14 import java.io.* ; 15 import j ava.net.* ; 16 17 public class TCPClientl 18 { 19 public static void main(String[] args) 20 { 21 InputStream is = null ; 22 OutputStream os = null; 23 Socket sock = null; 24 String addr = null; 25 String res = null; 26 String send = null; 27 String tmp = null ; 28 byte[] recv = new byte[4096]; 29 int port = 0; 30 int len = 0; 31 32 if(args.length != 3) 33 { 34 System.err.println(“usage: java TCPClientl” + 35 “ <target_ip> <target_port>“ + 36 “ <resource>.”); 37 38 System.err.println(“Example: java TCPClientl” + 39 “127.0.0.1 80 /”); 40 41 System.exit(1); 42 } 43 44 addr = args [0]; 45 tmp = args [1]; 46 res = args[2]; 47 48 try 49 { 50 // convert port value to integer 51 port = Integer.parselnt(tmp); 52 53 // connect to IP address and port 54 sock = new Socket(addr, port); 55 56 // get connection input & output streams 57 is = sock.getInputStream (); 58 os = sock.getOutputStream(); 59 60 // no exception thrown, connection established 61 send = “GET “ + res + * HTTP/1.0\r\n\r\n” ; 62 63 // send HTTP request 64 os.write(send.getBytes()); 65 66 // read response 67 len = is.read(recv); 68 69 // close connection 70 sock.close(); 71 72 // print results 73 if(len > 0) 74 { 75 // convert recv’d bytes to string.. 76 tmp = new String(recv) ; 77 78 // display via stdout 79 System.out.println(tmp ); 80 } 81 } 82 catch (NumberFormatException nfe) 83 { 84 // non-numeric port value? 85 System.err.println(“NumberFormatException:” 86 +nfe.getMessage()); 87 } 88 catch (IOException ioe) 89 { 90 // connection failed? 91 System.err.println(“IOException:” 92 + ioe.getMessage()); 93 } 94 } 95 }
|