Skip to Content

 

Recap

I am writing a ray tracer with ABAP by studying the book “Ray Tracing from the Ground Up“.
A ray tracer is able to create computer generated imagery.
If you like to know more about my motivation behind this endeavour, check out my first blog.

In my third blog I assembled the various classes converted from C++ and made the ray tracer run for the first time. The result was a red sphere, which I was able to display on a dynpro screen with the help of the auxiliary class ZCL_ART_BITMAP.

 

Objectives

In this fourth blog I am going to enable my ray tracer to render multiple objects.

It was and still will be necessary to convert C++ methods to ABAP while working with this book.
Because I felt not as confident with the conversion, I looked deeper into the topic of method parameter passing. I am going to share my findings with you here.

 

Tracer

To render multiple objects with my ray tracer a couple of adjustments had to be made.

First of all I needed a new tracer. The tracer is an “abstraction [layer] between the render_scene [method], which computes the origin and direction of each ray, and the code that determines what happens to the rays”. (Kevin Suffern, 2007, p. 70)

METHOD trace_ray.
  DATA(shade_rec) = _world->hit_bare_bones_objects( i_ray ).

  IF shade_rec->hit_an_object = abap_true.     
    r_color = shade_rec->color.
  ELSE. 
    r_color = zcl_art_rgb_color=>new_copy( me->_world->background_color ).
  ENDIF.
ENDMETHOD.

 

The second essential part is the hit_bare_bones_objects method inside the class zcl_art_world:

METHOD hit_bare_bones_objects.
  DATA:
    t    TYPE decfloat16,
    tmin TYPE decfloat16 VALUE '10000000000'.

  r_shade_rec = zcl_art_shade_rec=>new_from_world( me ).

  LOOP AT _objects ASSIGNING FIELD-SYMBOL(<object>).
    <object>->hit(
	  EXPORTING
	    i_ray = i_ray
	  IMPORTING
	    e_tmin = t
	    e_hit = DATA(hit)
	  CHANGING
	    c_shade_rec = r_shade_rec ).

    IF hit = abap_true AND ( t < tmin ).
	  r_shade_rec->hit_an_object = abap_true.
	  tmin = t.
	  r_shade_rec->color = <object>->get_color( ).
    ENDIF.
  ENDLOOP.
ENDMETHOD.

This code cycles through all the objects in the scene and returns the hit point which is closest to the “camera” via a shading record instance (shade_rec).

These were the two main ingredients to the feature.

I was able then to create and position as many objects as I liked. But after some time playing around with this new possibility, I realized how exhausting it is, doing all that manually myself.

So I thought, why not use a black and white mask to decide where to put spheres and let the computer create and position them for me.

 

The Mask

The knowledge, that I gained from outputting the rendered image as bitmap to the dynpro UI, helped me a lot in this matter.

I brought some variance into the game, by randomizing the spheres sizes and colors and how many pixels I am sampling.

I assembled the results in an animated gif for you, so you can better see how the mask and the altering of certain parameters affected the outcome.

 

The images consist of up to 1000+ spheres and the rendering time went up to over an hour due to the inefficacy of my current geometry handling.

Lets get on to the second topic of my blog. The fun part for all the nitpickers of you. 😉

 

Language Differences

I am realizing, that writing blogs is dramatically improving the quality of my ray tracer. It takes a lot more time and effort to really get the content of my blogs straight.
An initial idea, like “ahh let’s write a bit about method parameter conversion from C++ to ABAP” explodes into a scrollbar which suddenly carries from here to the moon and back.
Often this in-depth examination results into an issue ticket or more being created in gitHub to improve my ray tracer.

 

Method Parameter

Translating method signatures from C++ to ABAP is something I continuously need to do while working with the book.

There are plenty of keywords used in a method declaration, which can influence the behaviour in subtile and/or significant ways.

In this blog I am going to concentrate on parameter passing, as otherwise this article goes out-of-hand (again).

Keywords like IMPORTING, EXPORTING and so forth describe the role and changeability of a parameter passed to and used by a method. These keywords are often not as explicitly articulated in other programming languages.

The common categories are more often referred to as input, output or input-output parameter.

The following table maps, for the sake of completeness, the common categories to the ABAP keywords.

Input Output Input-Output
IMPORTING X
EXPORTING X
CHANGING X
RETURNING X

 

Pass-By-Value, -Reference and -Pointer

C++ supports three ways of passing parameters from the caller into a method: by value, reference and pointer, while ABAP supports two: by VALUE(...) and by REFERENCE(...).

One lesson I learnt was the following: If in C++ neither a reference nor a pointer parameter is used, then I have to implement pass-by-value instead of pass-by-reference.
From an ABAP perspective I then explicitly need VALUE(...) instead of REFERENCE(...).

ABAP – Keyword Documentation: Pass-By-Value and Pass-By-Reference

That table helped me in the beginning to resolve the pass-by types of C++ to ABAP in comparison with their field of use reflected by the keywords (IMPORTING, …)

Pass-By-Value Pass-By-Reference (&) Pass-By-Pointer (*)
IMPORTING VALUE(...)
(possible for simple types,
with helper functionality)
REFERENCE(...) REFERENCE(...)
EXPORTING REFERENCE(...) REFERENCE(...)
CHANGING REFERENCE(...) REFERENCE(...)
RETURNING VALUE(...)

 

Const Parameters

const is a keyword which tells the compiler, that the parameter cannot be changed – is constant.
There are exceptions from this rule, but I will not dive into these here.

It’s even possible in C++ to return a constant value from a method, which cannot be changed afterwards.

An output or input-output parameter can never be const.

An input parameter should be const, but that is not enforced by C++.

 

Returning

The void keyword lets you instantly know, that this method doesn’t have a returning parameter in stock.

If anything else than void is specified before the method name, I have my return type and know that a returning value is needed.

//None
void foobar();

//Value
int foobar();

//Reference
int& foobar();

//Pointer
int* foobar();

Code examples: void, value, reference, pointer

"None
METHODS foobar.

"Value
METHODS foobar
  RETURNING
    VALUE(r_result) TYPE int4.

"Reference
METHODS foobar
  RETURNING
    VALUE(r_result) TYPE REF TO int4.

"Pointer 
"Doesn't exist in ABAP. 
"Same approach like Reference taken, with 'TYPE REF TO'

Code examples: void, value, reference

 

Importing

Seeing const in the signature is an explicit indicator, that the parameter is an importing parameter. Cause in C++ the keyword const guarantees that the parameter cannot be altered by the method.

However, if the developer got sloppy, then even without the const, the parameter might be an importing one.

//Pass-By-Value
void foobar(const int value);

//Pass-By-Reference
void foobar(const int& value);

//Pass-By-Pointer
void foobar(const int* value);

Code examples: value, reference, pointer

"Pass-By-Value
METHODS foobar
  IMPORTING
    VALUE(i_value) TYPE int4.

"Pass-By-Reference
METHODS foobar
  IMPORTING
    REFERENCE(i_value) TYPE int4.

"Pass-By-Pointer 
"Doesn't exist in ABAP. Same approach like Pass-By-Reference taken.

Code examples: value, reference

 

Exporting

The ampersand (&) or star (*) without the keyword const can be an exporting parameter, but you need to check inside the method, whether more than assignment is going on in there.

//Pass-By-Value 
//Cannot be used, as the change would be lost when returned to the caller.

//Pass-By-Reference
void foobar(int& value);

//Pass-By-Pointer
void foobar(int* value);

Code examples: reference, pointer

"Pass-By-Value
METHODS foobar
  EXPORTING
    VALUE(e_value) TYPE int4.

"Pass-By-Reference
METHODS foobar
  EXPORTING
    REFERENCE(e_value) TYPE int4.

"Pass-By-Pointer
"Doesn't exist in ABAP. Same approach like Pass-By-Reference taken.

Code examples: value, reference

 

Changing

Very similar to Exporting, I need to make sure that beside an assignment also the parameter value is used within the method.

//Pass-By-Value
//Cannot be used, as the change would be lost when returned to the caller.

//Pass-By-Reference
void foobar(int& value);

//Pass-By-Pointer
void foobar(int* value);

Code examples: reference, pointer

"Pass-By-Value
METHODS foobar
  CHANGING
    VALUE(c_value) TYPE int4.

"Pass-By-Reference
METHODS foobar
  CHANGING
    REFERENCE(c_value) TYPE int4.

"Pass-By-Pointer
"Doesn't exist in ABAP. Same approach like Pass-By-Reference taken.

Code examples: value, reference

 

Pass-By-Value of Classes/Interfaces

When plotting this blog, I found very confusing to view the pass-by types and parameter types in conjunction. Especially, the pass-by-value of a parameter which refers to a class.

class Foo {
public:
    void foobar(const Bar value);
};

void Foo::foobar(const Bar value) {
    std::cout << value.getValue();
}

The C++ code above invokes the copy constructor. The method receives a copy of the instance.

The copy constructor is a constructor which creates an object by initializing it with an object of the same class, which has been created previously. 

Tutorials Point, “C++ Copy Constructor”, Accessed 21.10.2017

Passing instances by value is not possible with ABAP. Only a copy of the reference to the instance can be passed. Meaning, if I want a copy of the instance, I need to create it beforehand by my own.

But I wondered, is there a difference in ABAP between passing by value and passing by reference of instances?

CLASS cl_foo DEFINITION.
  PUBLIC SECTION.
    METHODS:
      pass_class_by_reference
        IMPORTING
          REFERENCE(i_value) TYPE REF TO cl_bar,

      pass_class_by_value
        IMPORTING
          VALUE(i_value) TYPE REF TO cl_bar.

ENDCLASS.

Full implementation

 

Yes: Pass-by-value consumes in my example 16 bytes more memory than pass-by-reference.

 

Please right click on the following screenshots and
choose “Open image in new tab” to see them in appropriate size.

Pass-By-Reference
160 Bytes 352 Bytes

 

Pass-By-Value
160 Bytes 368 Bytes

 

I assume that the 16 bytes are used to initiate a second reference on the stack memory, as illustrated below.

 

Conclusion

This blog concludes chapter 3 of the book.

My ray tracer is now able to render any number of objects.
It doesn’t need to be spheres, it just needs to inherit from zcl_art_geometric_object.
This was achieved by implementing a new tracer (zcl_art_multiple_objects).

Now I am also more confident in translating C++ method parameters to ABAP, after studying the behaviour when passing variables into methods with both languages.

I will need to keep an eye open for pass-by-value of class instances, as this triggers the copy-constructor in C++ behind the scenes, which doesn’t exist in ABAP.

 

What’s Next

I will start implementing anti-aliasing to my ray-tracer in the next blog.
This will help with getting smooth edges instead of the jagged edges.

Thanks,
André

 

Resource

https://stackoverflow.com/questions/6900035/in-out-parameters-and-how-to-work-with-them-in-c

https://stackoverflow.com/questions/334856/are-there-benefits-of-passing-by-pointer-over-passing-by-reference-in-c

https://stackoverflow.com/questions/3784114/how-to-pass-optional-arguments-to-a-method-in-c

http://www.cplusplus.com/articles/z6vU7k9E/

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply