so, I am writing a couple of functions so to run GLSL fragment shaders on CPU. I implemented all the basic mathematical functions to do so. However I can't even get the simplest stuff to execute correctly. I was able to trace it down to either of these functions:
template<unsigned vsize>
_SHADERH_INLINE
vec<float, vsize> normalize(const vec<float, vsize>& vs) {
vec<float, vsize> fres;
float mod = 0.0f;
for (unsigned i = 0; i < vsize; ++i) {
mod += vs[i] * vs[i];
}
float mag = glsl::sqrt(mod);
if (mag == 0) {
std::logic_error("In normalize the input vector is a zero vector");
}
for (unsigned i = 0; i < vsize; ++i) {
fres[i] = vs[i] / mag;
}
return fres;
}
template<unsigned vsize>
_SHADERH_INLINE
vec<float, vsize> length(const vec<float, vsize>& vs) {
float fres = 0;
for (unsigned it = 0; it != vsize; it++) {
fres += vs[it] * vs[it];
}
return glsl::sqrt(fres);
}
template<unsigned vsize>
_SHADERH_INLINE
vec<float, vsize> dot(const vec<float, vsize>& vs1, const vec<float, vsize>& vs2) {
return std::inner_product(vs1.begin(), vs1.end(), vs2.begin(), 0);
}
But it could still be something in my vec implementation (pretty certain it's not).
So, does anyone see anything wrong with the code above or something that does not align with glsl behavior? If nobody is able to find anything wrong there, I will make a follow-up question with my vec implementation.
There are few things that may be the origin of the problems.
After a bit of testing, it turned out that your dot
function fails, because you are providing an integer value 0
as initial value in your call to inner_product
, which results in wrong calculations. See this post for the reason: Zero inner product when using std::inner_product
Simply write 0.0f
to ensure a float as initial value for the accumulator :
vec<float, vsize> dot(const vec<float, vsize>& vs1, const vec<float, vsize>& vs2) {
return std::inner_product(vs1.begin(), vs1.end(), vs2.begin(), 0.0f);
}
More generally, I recommend you to always write any hardcoded value with clear indication of its type.
You can also manually write your dot product as well, as you did in normalize
and length
:
vec<float, vsize> dot(const vec<float, vsize>& vs1, const vec<float, vsize>& vs2) {
float f = 0.0f;
for (unsigned i = 0; i < vsize; ++i) {
f += vs1[i] * vs2[i];
}
return f;
}
In the normalize
function, you are not actually throwing any error when mag == 0
. When passing a null vector to normalize
, it returns (-nan, -nan, -nan) instead of throwing the error you want. std::logic_error
does not throw any error, it creates an object to be thrown (see : https://en.cppreference.com/w/cpp/error/logic_error).
Thus instead of writing :
if (mag == 0) {
// Doesn't do anything...
std::logic_error("In normalize the input vector is a zero vector");
}
You must write :
if (mag == 0.0f) {
// Actually throws a `logic_error` exception
throw std::logic_error("In normalize the input vector is a zero vector");
}
vec
typesThis depends on your implementation of vec
. length
and dot
return scalar values (float
) and not vectors. Since (to my understanding) you didn't mention compilation errors, I assume your vec
type can handle floats as 1-component vectors. Be sure that this is indeed working.
Here is the code I used to test your functions. I quickly implemented a simple vec
class and adapted your functions to this type :
#include <iostream>
#include <vector>
#include <math.h>
#include <numeric>
#include <stdexcept>
class vec {
public:
int vsize;
std::vector<float> vals;
vec(int s) : vsize(s){
vals = std::vector<float>(vsize, 0.0f);
}
};
void print_vec(vec& v){
for(unsigned i = 0; i < v.vsize; i++){
std::cout << v.vals[i] << " ";
}
std::cout << std::endl;
}
vec normalize(const vec& vs) {
vec fres(vs.vsize);
float mod = 0.0f;
for (unsigned i = 0; i < vs.vsize; ++i) {
mod += vs.vals[i] * vs.vals[i];
}
float mag = sqrt(mod);
if (mag == 0.0f) {
throw std::logic_error("In normalize the input vector is a zero vector");
}
for (unsigned i = 0; i < vs.vsize; ++i) {
fres.vals[i] = vs.vals[i] / mag;
}
return fres;
}
float length(const vec& vs) {
float fres = 0;
for (unsigned it = 0; it != vs.vsize; it++) {
fres += vs.vals[it] * vs.vals[it];
}
return sqrt(fres);
}
float dot(const vec& vs1, const vec& vs2) {
return std::inner_product(vs1.vals.begin(), vs1.vals.end(), vs2.vals.begin(), 0.0f);
}
In main
I simply tested some hardcoded vectors and printed the results of normalize
, length
and dot
. I ran the code on https://www.onlinegdb.com/online_c++_compiler