Trouble when rendering voxels with pathtracing - opencl

I'm currently working on a pathtracer in c and open cl.
I'm using this algorithm for rendering. The first collision works well, however, from the second collision onwards there is a dark shadow on the lower side of the voxels.
This is the color of the voxel the initial ray hits:
result
This is the color of the voxel that the second ray hits:
result
And this is the result after rendering to a depth of 1000:
result
This is the code I used (openCL):
int cast_ray(Renderer *r, Ray ray, float3 *hitPos, int3 *normal, Material *material) {
int3 voxel = convert_int3(ray.origin);
int3 step = {
(ray.direction.x >= 0) ? 1 : -1,
(ray.direction.y >= 0) ? 1 : -1,
(ray.direction.z >= 0) ? 1 : -1
};
float3 tMax = {
(ray.direction.x != 0) ? (voxel.x + step.x - ray.origin.x) / ray.direction.x : MAXFLOAT,
(ray.direction.y != 0) ? (voxel.y + step.y - ray.origin.y) / ray.direction.y : MAXFLOAT,
(ray.direction.z != 0) ? (voxel.z + step.z - ray.origin.z) / ray.direction.z : MAXFLOAT
};
float3 tDelta = {
(ray.direction.x != 0) ? 1 / ray.direction.x * step.x : MAXFLOAT,
(ray.direction.y != 0) ? 1 / ray.direction.y * step.y : MAXFLOAT,
(ray.direction.z != 0) ? 1 / ray.direction.z * step.z : MAXFLOAT
};
int side;
while(1) {
if(tMax.x < tMax.y) {
if(tMax.x < tMax.z) {
voxel.x += step.x;
tMax.x += tDelta.x;
side = 0;
} else {
voxel.z += step.z;
tMax.z += tDelta.z;
side = 2;
}
} else {
if(tMax.y < tMax.z) {
voxel.y += step.y;
tMax.y += tDelta.y;
side = 1;
} else {
voxel.z += step.z;
tMax.z += tDelta.z;
side = 2;
}
}
if(out_of_scene(r, voxel))
return 0;
MaterialID id = get_material_ID(r, voxel);
if(id == 0)
continue;
*material = get_material(r, id);
switch(side) {
case 0:
hitPos->x = (float)voxel.x;
hitPos->y = ray.origin.y + (hitPos->x - ray.origin.x) * ray.direction.y / ray.direction.x;
hitPos->z = ray.origin.z + (hitPos->x - ray.origin.x) * ray.direction.z / ray.direction.x;
*normal = (int3){-step.x, 0, 0};
break;
case 1:
hitPos->y = (float)voxel.y;
hitPos->x = ray.origin.x + (hitPos->y - ray.origin.y) * ray.direction.x / ray.direction.y;
hitPos->z = ray.origin.z + (hitPos->y - ray.origin.y) * ray.direction.z / ray.direction.y;
*normal = (int3){0, -step.y, 0};
break;
case 2:
hitPos->z = (float)voxel.z;
hitPos->y = ray.origin.y + (hitPos->z - ray.origin.z) * ray.direction.y / ray.direction.z;
hitPos->x = ray.origin.x + (hitPos->z - ray.origin.z) * ray.direction.x / ray.direction.z;
*normal = (int3){0, 0, -step.z};
break;
}
return 1;
}
}
float3 get_color(Renderer *r, Ray ray) {
float3 mask = 1;
float3 color = 0;
int maxDepth = 1000;
for(int i = 0; i < maxDepth; i++) {
float3 hitPos;
int3 iNormal;
Material material;
if(cast_ray(r, ray, &hitPos, &iNormal, &material)) {
float3 fNormal = convert_float3(iNormal);
if(material.type == 1) {
color = mask * material.color;
break;
} else if(material.type == 2) {
float3 direction = fNormal + random_unit_vector(r->rng);
ray = (Ray){hitPos, direction};
mask *= material.color;
} else if(material.type == 3) {
float3 direction = reflection_dir(ray.direction, fNormal) + random_unit_vector(r->rng) * material.fuzzyness;
ray = (Ray){hitPos, direction};
mask = mask * (1 - material.tint) + mask * material.color * material.tint;
}
} else {
color = mask * r->bgColor;
break;
}
// if(i == 1)
// return material.color;
}
return color;
}
I think that the problem is that the new origin of the ray is somehow not correct, but I can't find a way to fix it.

Related

I would like to simulate gravitation between particles, but I think I forgot something?

So, for each star, i compare this one to all other stars to calculate his speed, velocity, etc.
But that didn't work, I'm not too strong in maths and I think my formula is maybe wrong? idk why that didn't work here my code :
//for each star I compare to all other stars
for(let i = 0; i < pos.length; i ++) {
for (let j = 0; j < pos.length; j ++){
if (i !== j){
// Formula part
const vector = compute_interaction(pos[i], pos[j], 1.0);
accelerations[i].x += vector.x;
accelerations[i].y += vector.y;
accelerations[i].z += vector.z;
break;
}
}
}
for (let i = 0 ; i<accelerations.length ; i++){
speedStars[i].x += accelerations[i].x * 0.001;
speedStars[i].y += accelerations[i].y * 0.001;
speedStars[i].z += accelerations[i].z * 0.001;
}
for (let i = 0 ; i<speedStars.length ; i++){
const i3 = i*3;
starsPositions[i3] += speedStars[i].x * 0.001;
starsPositions[i3 + 1] += speedStars[i].y * 0.001;
starsPositions[i3 + 2] += speedStars[i].z * 0.001;
}
function compute_interaction(currentPosition, positionOtherStar, smoothing_length)
{
const vector = new THREE.Vector3(positionOtherStar.x - currentPosition.x, positionOtherStar.y - currentPosition.y, positionOtherStar.z - currentPosition.z).normalize();
let x = vector.x / (Math.pow(positionOtherStar.x,2.0) - Math.pow(currentPosition.x,2.0)+ smoothing_length)
let y = vector.y / (Math.pow(positionOtherStar.y,2.0) - Math.pow(currentPosition.y,2.0)+ smoothing_length)
let z = vector.z / (Math.pow(positionOtherStar.z,2.0) - Math.pow(currentPosition.z,2.0)+ smoothing_length)
return new THREE.Vector3(x, y, z);
}
Here the CodePen: https://codepen.io/n0rvel/pen/ExEXbYN?editors=0010
Here is the formula/code logic I found on one OpenCL program that works:
Probably, the compute_interaction() function should be:
function compute_interaction(currentPosition, positionOtherStar, smoothing_length)
{
//const vector = new THREE.Vector3(positionOtherStar.x - currentPosition.x, positionOtherStar.y - currentPosition.y, positionOtherStar.z - currentPosition.z).normalize();
//let x = vector.x / (Math.pow(positionOtherStar.x,2.0) - Math.pow(currentPosition.x,2.0)+ smoothing_length)
//let y = vector.y / (Math.pow(positionOtherStar.y,2.0) - Math.pow(currentPosition.y,2.0)+ smoothing_length)
//let z = vector.z / (Math.pow(positionOtherStar.z,2.0) - Math.pow(currentPosition.z,2.0)+ smoothing_length)
//return new THREE.Vector3(x, y, z);
const vector = new THREE.Vector3().subVectors(positionOtherStar, currentPosition);
return vector.normalize().divideScalar(vector.lengthSq() + smoothing_length);
}

How to convert Ximea xiAPI camera data into QImage?

I have data from a camera in mono 8bit.
This is converted into an int vector using
std::vector<int> grayVector(size);
// convert / copy pointer data into vector: 8 bit
if (static_cast<XI_IMG_FORMAT>(format) == XI_MONO8)
{
quint8* imageIterator = reinterpret_cast<quint8*> (pMemVoid);
for (size_t count = 0; count < size; ++count)
{
grayVector[count] = static_cast<int>(*imageIterator);
imageIterator++;
}
}
Next, I need to convert this into a QImage. If I set the image format to QImage::Format_Mono the app crashes. With QImage::Format_RGB16 I get strippes, and with QImage::Format_RGB32 everything is black.
I would like to know how to do this the best, efficient and correct way?
// convert gray values into QImage data
QImage image = QImage(static_cast<int>(sizeX), static_cat<int>(sizeY), QImage::Format_RGB16);
for ( int y = 0; y < sizeY; ++y )
{
int yoffset = sizeY*y;
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)) ;
for ( int x = 0; x < sizeX ; ++x )
{
int pos = x + yoffset;
int color = grayVector[static_cast<size_t>(pos)];
*line++ = qRgb(color, color, color);
}
}
The conversion to int is unnecessary and you do it in a very inefficient way; all you need is to use the QImage::Format_Grayscale8 available since Qt 5.5 (mid-2015).
Anyway, what you really want is a way to go from XI_IMG to QImage. The default BP_UNSAFE buffering policy should be adequate - the QImage will do a format conversion, so taking the data from XiApi's internal buffer is OK. Thus the following - all of the conversions are implemented in Qt and are quite efficient - much better than most any naive code.
I didn't check whether some Xi formats may need a BGR swap. If so, then the swap can be set to true in the format selection code and the rest will happen automatically.
See also: xiAPI manual.
static QVector<QRgb> grayScaleColorTable() {
static QVector<QRgb> table;
if (table.isEmpty()) {
table.resize(256);
auto *data = table.data();
for (int i = 0; i < table.size(); ++i)
data[i] = qRgb(i, i, i);
}
return table;
}
constexpr QImage::Format grayScaleFormat() {
return (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
? QImage::Format_Grayscale8
: QImage::Format_Indexed8;
}
QImage convertToImage(const XI_IMG *src, QImage::Format f) {
Q_ASSERT(src->fmt == XI_MONO16);
Q_ASSERT((src->padding_x % 2) == 0);
if (src->fmt != XI_MONO16) return {};
const quint16 *s = static_cast<const quint16*>(src->bp);
const int s_pad = src->padding_x/2;
if (f == QImage::Format_BGR30 ||
f == QImage::Format_A2BGR30_Premultiplied ||
f == QImage::Format_RGB30 ||
f == QImage::Format_A2RGB30_Premultiplied)
{
QImage ret{src->width, src->height, f};
Q_ASSERT((ret->bytesPerLine() % 4) == 0);
const int d_pad = ret->bytesPerLine()/4 - ret->width();
quint32 *d = (quint32*)ret.bits();
if (s_pad == d_pad) {
const int N = (src->width + s_pad) * src->height - s_pad;
for (int i = 0; i < N; ++i) {
quint32 const v = (*s++) >> (16-10);
*d++ = 0xC0000000 | v << 20 | v << 10 | v;
}
} else {
for (int j = 0; j < src->height; ++j) {
for (int i = 0; i < src->width; ++i) {
quint32 const v = (*s++) >> (16-10);
*d++ = 0xC0000000u | v << 20 | v << 10 | v;
}
s += s_pad;
d += d_pad;
}
}
return ret;
}
QImage ret{src->width, src->height, grayScaleFormat()};
const int d_pad = ret->bytesPerLine() - ret->width();
auto *d = ret.bits();
if (s_pad == d_pad) {
const int N = (src->width + s_pad) * src->height - s_pad;
for (int i = 0; i < N; ++i) {
*d++ = (*s++) >> 8;
} else {
for (int j = 0; j < src->height; ++j) {
for (int i = 0; i < src->width; ++i)
*d++ = (*s++) >> 8;
s += s_pad;
d += d_pad;
}
}
return ret;
}
QImage fromXiImg(const XI_IMG *src, QImage::Format dstFormat = QImage::Format_ARGB32Premultiplied) {
Q_ASSERT(src->width > 0 && src->height > 0 && src->padding_x >= 0 && src->bp_size > 0);
Q_ASSERT(dstFormat != QImage::Format_Invalid);
bool swap = false;
int srcPixelBytes = 0;
bool externalConvert = false;
QImage::Format srcFormat = QImage::Format_Invalid;
switch (src->fmt) {
case XI_MONO8:
srcPixelBytes = 1;
srcFormat = grayScaleFormat();
break;
case XI_MONO16:
srcPixelBytes = 2;
externalConvert = true;
break;
case XI_RGB24:
srcPixelBytes = 3;
srcFormat = QImage::Format_RGB888;
break;
case XI_RGB32:
srcPixelBytes = 4;
srcFormat = QImage::Format_RGB32;
break;
};
if (srcFormat == QImage::Format_Invalid && !externalConvert) {
qWarning("Unhandled XI_IMG image format");
return {};
}
Q_ASSERT(srcPixelBytes > 0 && srcPixelBytes <= 4);
int bytesPerLine = src->width * srcPixelBytes + src->padding_x;
if ((bytesPerLine * src->height - src->padding_x) > src->bp_size) {
qWarning("Inconsistent XI_IMG data");
return {};
}
QImage ret;
if (!externalConvert)
ret = QImage{static_cast<const uchar*>(src->bp), src->width, src->height,
bytesPerLine, srcFormat};
else
ret = convertToImage(src, dstFormat);
if (ret.format() == QImage::Format_Indexed8)
ret.setColorTable(grayScaleColorTable());
if (ret.format() != dstFormat)
ret = std::move(ret).convertToFormat(dstFormat);
if (swap)
ret = std::move(ret).rgbSwapped();
if (!ret.isDetached()) // ensure that we don't share XI_IMG's data buffer
ret.detach();
return ret;
}

Algorithm implemented with openCL working till size exceeds 768

I've implemented sorting algorithm using openCL. Its using one work group per array to sort (arrays are connected in __global float *array, all have the same size).
Im testing results using 200 random arrays and result are deterministic.
With one parameter, its working correctly till array size exceeds of array 768
With two parameters, its working correctly till arrays size exceeds 768
With three parameters, its working correctly till arrays size exceeds 317
What could be the reason of correct processing of just 768 (CL_KERNEL_WORK_GROUP_SIZE returns 1024 elements). Is it some memory constraints? What is the best way of invastigation such issue?
Gpu specs (4th answer):
Kernel code below:
__kernel void assort(
__global float *array,
__local float *currentOutput,
__local float *stimulations,
__local int *noOfValuesAdded,
__local float *addedValue,
__local float *positionToInsert,
__local int *activatedIdx,
__local float *range,
int size
) {
int id = get_local_id(0);
int gid = get_group_id(0);
if (id == 0)
{
if (array[gid*size]<array[gid*size+1])
{
currentOutput[0] = array[gid*size];
currentOutput[1] = array[gid*size + 1];
}
else
{
currentOutput[1] = array[gid*size];
currentOutput[0] = array[gid*size + 1];
}
noOfValuesAdded[0] = 2;
}
barrier(CLK_LOCAL_MEM_FENCE);
for (int i = 2; i < size; i++)
{
int maxIdx = noOfValuesAdded[0] - 1;
if (id == 0)
{
addedValue[0] = array[gid*size + i];
positionToInsert[0] = -100.0f;
activatedIdx[0] = -2;
range[0] = currentOutput[maxIdx] - currentOutput[0];
}
barrier(CLK_LOCAL_MEM_FENCE);
if (id < noOfValuesAdded[0])
{
if (id == 0)
{
stimulations[id] = (currentOutput[maxIdx] - addedValue[0]) / range[0];
float stimulation = stimulations[id];
if ( fabs(stimulation - 1.0f) < 0.000001)
activatedIdx[0] = 0;
else if (stimulation > 1.0f)
{
activatedIdx[0] = -1;
}
}
else if (id == maxIdx)
{
stimulations[maxIdx] = (addedValue[0] - currentOutput[0]) / range[0];
float stimulations = (addedValue[0] - currentOutput[0]) / range[0];
if ( fabs(stimulations - 1.0f) < 0.000001 )
activatedIdx[0] = maxIdx;
else
if (stimulations > 1)
activatedIdx[0] = maxIdx + 1;
}
else
{
stimulations[id] = 1.0f - (fabs((currentOutput[id] - addedValue[0])) / range[0]);
if ( fabs(stimulations[id] - 1.0f) < 0.000001)
activatedIdx[0] = id;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (activatedIdx[0] == -2 && id < noOfValuesAdded[0])
{
if (noOfValuesAdded[0] == 2)
{
positionToInsert[0] = 0.9f;
}
else if (id != 0 &&
id != maxIdx &&
stimulations[id] >= stimulations[(id - 1)] &&
stimulations[id] >= stimulations[(id + 1)] )
{
if ((1.0f - (fabs(currentOutput[(id - 1)] - currentOutput[id]) / range[0]) ) < stimulations[(id - 1)])
positionToInsert[0] = (float)id - 0.1f;
else
positionToInsert[0] = (float)id + 0.9f;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (activatedIdx[0] == -2)
{
if (id == 0 && positionToInsert[0] < -90.0f) // default value maintained
{
if (stimulations[0] > stimulations[1])
positionToInsert[0] = 0.9f;
else
positionToInsert[0] = (float)maxIdx - 0.1f;
}
}
else
{
if (activatedIdx[0] == -1)
positionToInsert[0] = -0.1f;
else if (activatedIdx[0] == (maxIdx + 1))
{
positionToInsert[0] = (float)maxIdx + 0.9f;
}
else
{
currentOutput[activatedIdx[0]] = addedValue[0];
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (positionToInsert[0] > -50.0f) // default value changed
{
float temp = 0.0f;
if ((float)id>positionToInsert[0])
{
temp = currentOutput[id];
currentOutput[id + 1] = temp;
}
barrier(CLK_LOCAL_MEM_FENCE);
if ((float)id > positionToInsert[0])
{
temp = currentOutput[id];
}
barrier(CLK_LOCAL_MEM_FENCE);
if (id == round(positionToInsert[0]))
{
currentOutput[id] = addedValue[0];
noOfValuesAdded[0] = noOfValuesAdded[0] + 1;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
}
barrier(CLK_LOCAL_MEM_FENCE);
array[gid*size + id] = currentOutput[id];
return;
}

SpriteKit - zRotaion with ratio

I'm stuck trying to make a analog clock where the hour hand is following the minute hand.
Tried all i can think off but the ratio is off.
Dragging the minute hand works correctly so i think it's the way i calculate the ratio of which the hour hand should rotate.
This is my most recent try of many...
Code:
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
CGFloat oldAngleInRadians = M_PI_2;
- (void)rotateNode:(SKSpriteNode *)node forTouch:(UITouch *)touch
{
if ([node isEqual:dial]) return;
CGPoint positionInDial = [touch locationInNode:dial];
float deltaY = positionInDial.y - minuteIndicator.anchorPoint.y;
float deltaX = positionInDial.x - minuteIndicator.anchorPoint.x;
CGFloat angleInRadians = atan2f(deltaY, deltaX);
// Minute hand rotation in this case
[node runAction:[SKAction rotateToAngle:angleInRadians - (M_PI / 2) duration:0]];
if (self.difficulty == kMediumDifficulty ) {
[self updateHourhandAngleWithNewAngle:angleInRadians oldAngle:oldAngleInRadians];
oldAngleInRadians = angleInRadians;
}
}
#pragma mark - Rotate hour hand with minute hand
- (void)updateHourhandAngleWithNewAngle:(CGFloat)newAngle oldAngle:(CGFloat)oldAngle
{
double newAngleDeg = RADIANS_TO_DEGREES(newAngle);
double oldAngleDeg = RADIANS_TO_DEGREES(oldAngle);
double differenceDeg = 0;
if (newAngleDeg > 0 && oldAngleDeg < 0) {
differenceDeg = oldAngleDeg - ( - 1 * newAngleDeg );
} else if (newAngleDeg < 0 && oldAngleDeg > 0) {
differenceDeg = fabsf(newAngleDeg) - oldAngleDeg;
} else {
differenceDeg = newAngleDeg - oldAngleDeg;
}
[hourIndicator runAction:[SKAction rotateByAngle:DEGREES_TO_RADIANS((differenceDeg / 12.0f)) duration:0]];
}
Not sure if I understood your problem correctly, but this is how I would make an analog clock using SpriteKit:
// MyScene.m
- (void) didMoveToView:(SKView *)view
{
[self.clock didEnterScene];
}
- (void) update:(CFTimeInterval)currentTime
{
CGFloat dt = 0.0;
if (self.lastUpdateInterval > 0)
{
dt = currentTime - self.lastUpdateInterval;
}
[self.clock update:dt];
self.lastUpdateInterval = currentTime;
}
// ClockNode.m
- (void) didEnterScene
{
SKSpriteNode* secHand = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(2, 100)];
SKSpriteNode* minHand = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(5, 100)];
SKSpriteNode* hourHand = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(10, 70)];
secHand.position = CGPointMake(CGRectGetMidX(self.scene.frame), CGRectGetMidY(self.scene.frame));
minHand.position = secHand.position;
hourHand.position = secHand.position;
secHand.anchorPoint = CGPointMake(0.5, 1.0);
minHand.anchorPoint = CGPointMake(0.5, 1.0);
hourHand.anchorPoint = CGPointMake(0.5, 1.0);
[self addChild:secHand];
[self addChild:minHand];
[self addChild:hourHand];
_secHand = secHand;
_minHand = minHand;
_hourHand = hourHand;
_secHand.zRotation = M_PI;
_minHand.zRotation = M_PI;
_hourHand.zRotation = M_PI;
_msec = 0.0f;
_sec = 0.0f;
_min = 0.0f;
_hour = 0.0f;
}
- (void) update:(CGFloat)dt
{
_msec += dt;
if (_msec >= 1)
{
_msec -= 1;
_sec += 1;
_secHand.zRotation -= 2*M_PI / 60;
}
if (_sec >= 60)
{
_sec -= 60;
_min += 1;
_minHand.zRotation -= 2*M_PI / 60;
}
if (_min >= 60)
{
_min -= 60;
_hour += 1;
_hourHand.zRotation -= 2*M_PI / 12;
}
if (_hour >= 12)
{
_hour -= 12;
}
}

Qt keeping mouse centered

I'm wondering if anyone has a trick to keep the mouse position centered in a (QGL)widget for Qt. I read that one could set the mouseposition after finding the delta, but this way works very buggy for me. Mouse events are not properly registered, any if they do, very jumpy.
void World::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
player->rotHorizontal += 360.0 * dx;
if(player->rotHorizontal < 0.0)
player->rotHorizontal += 360.0;
else if(player->rotHorizontal > +360.0)
player->rotHorizontal -= 360.0;
player->rotVertical += 360.0 * dy;
if (player->rotVertical > MAX_ROTATION_UP) {
player->rotVertical = MAX_ROTATION_UP;
} else if (player->rotVertical < -MAX_ROTATION_UP) {
player->rotVertical = -MAX_ROTATION_UP;
}
}
// int diffX = event->pos().x() - lastPos.x() % 20;
// int diffY = event->pos().y() - lastPos.y() % 20;
// if (diffY > 10 || diffX > 10 || diffY < -10 || diffX < -10) {
// QPoint glob = mapToGlobal(QPoint(this->pos().x() + width()/2, this->pos().y() + height()/2));
// QCursor::setPos(glob);
// }
lastPos = event->pos();
QGLWidget::mouseMoveEvent(event);
}
I commented out the code which would keep the mouse centered. If this would work, I would place it in the leftclick area.
Fixed:
void World::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
player->rotHorizontal += 360.0 * dx;
if(player->rotHorizontal < 0.0)
player->rotHorizontal += 360.0;
else if(player->rotHorizontal > +360.0)
player->rotHorizontal -= 360.0;
player->rotVertical += 360.0 * dy;
if (player->rotVertical > MAX_ROTATION_UP) {
player->rotVertical = MAX_ROTATION_UP;
} else if (player->rotVertical < -MAX_ROTATION_UP) {
player->rotVertical = -MAX_ROTATION_UP;
}
}
QPoint glob = mapToGlobal(QPoint(width()/2,height()/2));
QCursor::setPos(glob);
lastPos = QPoint(width()/2,height()/2);
QGLWidget::mouseMoveEvent(event);
}

Resources