Painter

  • 总体设计方案
    • 界面设计
    • 程序操作说明
    • 笔刷展示
    • 快捷键
    • 拓展绘画理解
    • 技法
    • 工具/载体
    • 局限性
    • 创作体验
    • 应用
    • 参考文献

总体设计方案

本程序使用openframeworks,fbo设置一块画布,所有效果均呈现在画布上;右边空白区域画矩形作为按钮,设置一个gui修改笔刷的大小和颜色。

ofFbo fbo;
ofxPanel gui;
ofParameter<int> scale;
ofParameter<ofColor> color;

由ofApp.cpp写主要代码,通过main.cpp运行:

int main( ){ofSetupOpenGL(1000,800, OF_WINDOW);          // <-------- setup the GL context// this kicks off the running of my app// can be OF_WINDOW or OF_FULLSCREEN// pass in width and height too:ofRunApp( new ofApp());}

界面设计

  1. 总界面设计:
  2. 左上角用来调整画笔的颜色、大小、透明度:
  3. 最右边呈现所有笔刷的按钮:

程序操作说明

点击对应按钮,显示对应的笔刷样式,用判断函数表示

int ofApp::judge()//判断鼠标是否点击到按钮
{if (mouseX > ofGetWidth() - 90 && mouseX < ofGetWidth() - 10){if (mouseY < 40 && mouseY>10){temp = 1;printf("clicked1    ");return temp;}else if (mouseY > 50 && mouseY < 80){temp = 2;printf("clicked2 ");return temp;}else if (mouseY > 610 && mouseY < 640){temp = 16;printf("clear   ");return temp;}}else {return temp = 0;}
}

对应的按钮执行对应的笔刷,用temp的值来判断

fbo.begin();
ofSetColor(color);//改变颜色
if (temp == 1)
{brush1();
}if (temp == 9)
{if (usecamera) {float rotateAmount = ofMap(ofGetMouseX(), 0, ofGetWidth(), 0, 360);ofVec3f furthestPoint;if (points.size() > 0) {furthestPoint = points[0];}else{furthestPoint = ofVec3f(x, y, 0);}ofVec3f directionToFurthestPoint = (furthestPoint - center);ofVec3f directionToFurthestPointRotated = directionToFurthestPoint.getRotated(rotateAmount, ofVec3f(0, 1, 0));camera.setPosition(center + directionToFurthestPointRotated);camera.lookAt(center);}//otherwise add points like beforeelse {ofVec3f mousePoint(x, y, 0);points.push_back(mousePoint);}
}if (temp == 16)
{eraser();
}else
{ofDrawCircle(0,0,0);//默认啥都不画
}fbo.end();
}

笔刷展示

  1. png
    导入一个ps的内置笔刷,这里只随机选用了其中一种笔刷,定义一个参数brushdist,让每个笔刷元素之间的间距成为一个定值,变量 lastX,lastY 用于保存上个笔刷的坐标位置,dist 代表上个笔刷坐标到当前鼠标坐标的距离。当 dist 小于笔刷间距 brushDist,就说明距离太靠近,不应该绘制下一个笔刷。这可以避免绘制过慢时导致笔刷过密。当 dist 大于等于 brushDist,才会开始绘制。
void ofApp::brush1()
{//ofDrawCircle(mouseX, mouseY, scale);int brushNum;float dist = ofDist(mouseX, mouseY, lastX, lastY);float angle = atan2(mouseY - lastY, mouseX - lastX);if (dist > brushDist){brushNum = int(dist / brushDist);float newX, newY;for (int i = 1; i <= brushNum; i++){float length = i * brushDist;newX = lastX + cos(angle)*length;newY = lastY + sin(angle)*length;ofSetColor(color);b1.draw(newX, newY, 2*scale, 2*scale);}lastX = newX;lastY = newY;}//b1.draw(mouseX, mouseY,scale,scale);
}

  1. opengl
    mask:笔刷涂过的地方会显现出下层的图片,寥寥几笔就可以创造出一幅巨作。设置一个maskfbo,笔刷绘制在mask上面,给出部分代码:
void ofApp::randomize(ofRectangle & viewport)
{// utlitly function to randomise a rectangleviewport.x = 0;viewport.y = 0;viewport.width = ofGetWidth();viewport.height = ofGetHeight();
}//--------------------------------------------------------------
void ofApp::randomizeViewports()
{//randomize(viewport2D);randomize(viewport3D);
}void ofApp::drawViewportOutline(const ofRectangle & viewport)
{ofPushStyle();ofFill();ofNoFill();ofSetColor(255);ofDrawRectangle(viewport);ofPopStyle();
}//opengl
void ofApp::brush2()
{       drawViewportOutline(viewport3D);camera.begin(viewport3D);   ofSetColor(color);ofDrawEllipse(mouseX, mouseY, scale,scale);camera.end();
}

  1. stars
    绘制多个初始颜色下的随机颜色的星星笔刷。记录鼠标的位置,绘制多个三角形,组成星星状,颜色范围是以0,255,255为主色调,其他颜色可以自定义:
void ofApp::brush3()
{int numTriangles = 10;int minOffset = 5;int maxOffset = 70;int alpha = 150;for (int t = 0; t < numTriangles; ++t){float offsetDistance = ofRandom(minOffset, maxOffset);ofVec2f mousePos(ofGetMouseX(), ofGetMouseY());ofVec2f p1(0, 6.25);ofVec2f p2(25, 0);ofVec2f p3(0, -6.25);float rotation = ofRandom(360);p1.rotate(rotation);p2.rotate(rotation);p3.rotate(rotation);ofVec2f triangleOffset(offsetDistance, 0.0);triangleOffset.rotate(rotation);p1 += mousePos + triangleOffset;p2 += mousePos + triangleOffset;p3 += mousePos + triangleOffset;ofColor init(0, 252, 255, alpha);//初始颜色ofColor colors(color);ofColor inbetween = init.getLerped(colors, ofRandom(1.0));ofSetColor(inbetween);ofDrawTriangle(p1, p2, p3);}
}

  1. emission
    做一个张开的花纹,颜色和大小根据系统的改变而改变:
void ofApp::brush4()
{int numLines = 30;//线的长度int minRadius = 5+scale;int maxRadius = 50+scale;for (int i = 0; i < numLines; i++) {float angle = ofRandom(ofDegToRad(360.0));float distance = ofRandom(minRadius, maxRadius);float xOffset = cos(angle) * distance;float yOffset = sin(angle) * distance;ofSetColor(color);//ofSetLineWidth(ofRandom(0));ofDrawLine(ofGetMouseX(), ofGetMouseY(), ofGetMouseX() + xOffset, ofGetMouseY() + yOffset);}
}

  1. rectangle
    矩阵,按下鼠标左键移动鼠标位置,图中的矩阵也会跟着发生改变:
void ofApp::brush5()
{ofColor whiteColor(255, 255, 255, 255); //ofColor fgColor = whiteColor;ofSetColor(color);ofSetRectMode(OF_RECTMODE_CORNER);ofFill();ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight()); ofSetRectMode(OF_RECTMODE_CENTER);ofSetColor(whiteColor);ofNoFill();ofPushMatrix();ofTranslate(ofGetWidth() / 2, ofGetHeight() / 2); for (int i = 0; i < 100; i++) {ofScale(1.1, 1.1);float time = ofGetElapsedTimef();float noise = ofSignedNoise(time * timeScale) * 20.0;ofRotate(noise);ofDrawRectangle(0, 0, 50, 50);}ofPopMatrix();
}

  1. spots
    画圈,颜色根据系统调整的颜色,由深到浅的变化,大小根据scale的大小变化:
void ofApp::brush6()
{if (ofGetMousePressed(OF_MOUSE_BUTTON_LEFT)) {int radiusStepSize = 5;  int maxOffsetDistance = 100; for (int radius = scale; radius > 0; radius -= radiusStepSize) {float angle = ofRandom(ofDegToRad(360.0));float distance = ofRandom(maxOffsetDistance);float xOffset = cos(angle) * distance;float yOffset = sin(angle) * distance;ofColor myOrange(255, 132, 0, 3);//初始颜色ofColor colors(color);ofColor inBetween = myOrange.getLerped(colors, ofRandom(1.0));ofSetColor(inBetween);ofDrawCircle(ofGetMouseX() + xOffset, ofGetMouseY() + yOffset, radius);}}
}

  1. lines
    以鼠标为中心点,按一定长度画直线
void ofApp::brush7()
{if (ofGetMousePressed(OF_MOUSE_BUTTON_LEFT)){ofSetColor(color);ofSetLineWidth(3);ofDrawLine(mouseX, mouseY, mouseX + ofSignedNoise(ofGetElapsedTimef() + 1.2) * 10*scale, mouseY + ofSignedNoise(ofGetElapsedTimef()) * 10*scale);}
}

  1. repeat
    重复7的笔刷,使图片紧密得具有层次感
void ofApp::initangle()
{glBegin(GL_POLYGON);glVertex2f(mouseX, mouseY);glVertex2f(10 + mouseX+scale, 10 + mouseY+scale);glVertex2f(20 + mouseX+scale, 10 + mouseY+scale);glVertex2f(10 + mouseX, 10 + mouseY);glEnd();glFlush();
}
void ofApp::display() { ofSetColor(color);initangle();glRotatef(10,0,0,0);  initangle();int i = 0;for (i = 0; i < 40; i++){glRotatef(10.0f, 0.0f, 0.0f, 1.0f);//glColor3f(1.0f, 1.0f, 1.0f);initangle();}//glutSwapBuffers();
}
void ofApp::brush8()
{display();}

  1. brush9
    一个流动性的笔刷,在界面中动起来,增添奇幻感:
if (temp == 9){if (usecamera) {float rotateAmount = ofMap(ofGetMouseX(), 0, ofGetWidth(), 0, 360);ofVec3f furthestPoint;if (points.size() > 0) {furthestPoint = points[0];}else{furthestPoint = ofVec3f(x, y, 0);}ofVec3f directionToFurthestPoint = (furthestPoint - center);ofVec3f directionToFurthestPointRotated = directionToFurthestPoint.getRotated(rotateAmount, ofVec3f(0, 1, 0));camera.setPosition(center + directionToFurthestPointRotated);camera.lookAt(center);}//otherwise add points like beforeelse {ofVec3f mousePoint(x, y, 0);points.push_back(mousePoint);}}

  1. cirles
    一个根据系统设置改变大小及颜色的圆形笔刷:
void ofApp::brush10()
{ofSetColor(color);//改变颜色ofDrawCircle(mouseX, mouseY, scale);
}

  1. dotted
    根据鼠标的动作画虚线:
void ofApp::brush11()
{ofSetColor(color);//改变颜色glLineWidth(2);glEnable(GL_LINE_STIPPLE);glLineStipple(2, 0x4444);glBegin(GL_LINES);glVertex2f(lastMouseX, lastMouseY);glVertex2f(mouseX, mouseY);glEnd();glEndList();
}

  1. dash-dotted
    点划线,在这里需要注意尽量让鼠标慢慢水平或竖直滑动:
void ofApp::brush12()
{ofSetColor(color);//改变颜色glLineWidth(2);glEnable(GL_LINE_STIPPLE);glLineStipple(2, 0xffcc);glBegin(GL_LINES);glVertex2f(lastMouseX, lastMouseY);glVertex2f(mouseX, mouseY);glEnd();glEndList();
}

  1. rectangle
    画部分矩形,呈现出部分马赛克样式:
void ofApp::brush13()
{ofSetColor(color);//改变颜色ofDrawRectangle(mouseX, mouseY, scale, scale);ofSetColor(201, 201, 201);ofDrawRectangle(mouseX, mouseY, scale / 2, scale / 2);
}

  1. snow
    调用OpenGL实现雪花,被颜料盘挡住无法截图,看算法代码:
void drawline(pt pt1, pt pt2)
{glLineWidth(3);glBegin(GL_LINES);glVertex2d(pt1.x, pt1.y);glVertex2d(pt2.x, pt2.y);glEnd();
}void drawkoch(pt pt1, pt pt2, int n)
{pt p1, p2, p3, p4, p5;p1.x = pt1.x;p1.y = pt1.y;p2.x = pt2.x;p2.y = pt2.y;if (n == 1){drawline(p1, p2);}if (n > 1){p3.x = p1.x + (-p1.x + p2.x) / 3;p3.y = p1.y + (-p1.y + p2.y) / 3;p4.x = p1.x + 2 * (-p1.x + p2.x) / 3;p4.y = p1.y + 2 * (-p1.y + p2.y) / 3;p5.x = (p4.x - p3.x) / 2 - (p4.y - p3.y)*sqrt(3) / 2 + p3.x;p5.y = (p4.y - p3.y) / 2 + (p4.x - p3.x)*sqrt(3) / 2 + p3.y;drawkoch(p1, p3, n - 1);drawkoch(p3, p5, n - 1);drawkoch(p5, p4, n - 1);drawkoch(p4, p2, n - 1);}if (n == 0)exit(0);}
void ofApp::brush14()
{pt p1, p2, p3;p1.x = 30;//30p1.y = 30;//30p2.x = 110;//110p2.y = 30;//30p3.x = 70;//70p3.y = 30 + 40 * sqrt(3);int n = 7;drawkoch(p1, p3, n);drawkoch(p3, p2, n);drawkoch(p2, p1, n);glFlush();
}
  1. Line
    画短线:
void ofApp::brush15()
{ofSetColor(color);//改变颜色glLineWidth(scale);glBegin(GL_LINES);glVertex2f(lastMouseX, lastMouseY);glVertex2f(mouseX, mouseY);glEnd();
}

  1. eraser
    设置一个橡皮,可调节大小,然后擦去已有图案,还原画布本来的颜色。

快捷键

  1. p/e
    导入了一个背景音乐:
if (key == 'p'){//开始播放音乐mysound.play();}if (key == 'e'){//停止播放音乐mysound.stop();}
  1. x
    截屏,结果保存到bin/data/picture.png
if (key == 'x'){//截图img.grabScreen(0, 0, ofGetWidth(), ofGetHeight());img.save("picture.png");}


3. ‘space’
开启摄像机,与brush9对应:

if (key == ' '){//是否开启摄像机usecamera = !usecamera;}
  1. f
    全屏显示
if (key == 'f'){//全屏显示ofToggleFullscreen();}

拓展绘画理解

绘画的意义亦包含利用此艺术行为再加上图形、构图及其他美学方法去达到表现出从事者希望表达的概念及意思这样一个目的。
拓展绘画可以理解为脱离了这些工具及行为,将绘画变得数字化,通过简单的鼠标操作,可以达成不同的艺术效果。

技法

传统的绘画是利用工具在画布上进行创作,是一个以表面作为支撑面,再在其之上加上颜色的行为,那些表面可以是纸张、油画布、木材、玻璃、漆器或混凝土等,加颜色的工具可以是画笔、也可以是刀、海绵或是油漆喷雾器等。
而拓展绘画是将绘画加以延伸,利用电脑及Adobe Photoshop或Corel Painter等软件将颜色画在数位的画布上,若有需要也可以印在真正的画布上。

工具/载体

传统绘画的工具是各类画笔,载体是画布,而现代的画家将其延伸,工具可以是沙、水、稻草或是木材等其他物体,拓展绘画最简洁的工具可以是鼠标,如果用在了别的数字设备上面,也可以是手、人、动物等其他各种有可能实现移动的物体上面,载体则由画布拓展到显示器、墙壁、空间等其他一切可以投影的地方,实现了工具、载体的多样性。

局限性

由于之前没有接触过open frameworks,创作时是从头开始学习他的基本语法,花的时间比较多。在写代码过程中,调用很多内部库和函数,于是代码量比较少。
没有写出更多复杂的笔刷样式,有些笔刷,例如画线,没有能够完美实现跟着鼠标画一条直线,反而是有点断断续续的小矩形形成的线段。在雪花部分,智能通过代码定义雪花的位置,通过xy坐标显示,还没有上升到跟着鼠标后面画。

创作体验

整个系统由仇佳琰写框架,然后分别写自己的代码部分,最后陈畅整合而成,仇佳琰写的部分是整个系统的主体组成,包括复杂的笔刷内容,陈畅整合完成之后,写完答题推文,最后两个人讨论加工。
整个创作过程的感觉就是,可以及时看到自己代码的运行成果,让人有写下去的动力,由于open frameworks的官方文档是英文的,所以英语要求还是比较高的。

应用

通过不同笔刷之间的组合,可以创作出不同的绘画作品,也可以通过别的设备,例如行人、声音等变换实现绘画系统的进一步升级。

参考文献

  1. 《以编程的思想来理解绘画—— (一)用”一笔画“表现“过程美”》
    https://blog.csdn.net/magicbrushlv/article/details/82634189
  2. 《什么是绘画?——以抽象思维理解绘画》
    https://blog.csdn.net/magicbrushlv/article/details/83858469
  3. openframeworks learning
    https://openframeworks.cc/learning/https://openframeworks.cc/learning/https://openframeworks.cc/learning/https://openframeworks.cc/learning/
  4. 场模型
    https://github.com/magicbrush/PixelArray
  5. 图元模型
    https://github.com/magicbrush/DrawingByCodingTutorialDemos
  6. 笔触模型
    https://github.com/magicbrush/StrokeAbstraction

“painter–openframeworks之绘画系统相关推荐

  1. 绘画系统(03):【类】QPainter[官翻]

    文章目录 详述 Settings Drawing 绘制Pixmaps和Images 绘制高分辨率的Pixmaps和Images 渲染质量 坐标转换 剪切 组合方式 局限性 Performance 公共 ...

  2. 绘画系统——P5.js

    本次互动媒体期末大作业是编写一个"绘画系统",该系统可提供一系列绘画材料(例如画笔/颜料/滤镜)给用户操作,以创作出动态/交互的绘画作品.该绘画系统是对"绘画" ...

  3. 互动媒体大作业——绘画系统

    绘画系统实验心得 这学期接触了"互动媒体技术"这门课,感触颇深.因为在第四维(根据爱因斯坦相对论),在时间上的重复,在传统静态绘画中是不可能实现的,但用编程就可以做到.这样一来,通 ...

  4. 纯属娱乐的涂色绘画系统

    P5.js 之秘密花园--自制线稿上色绘画系统 一.灵感来源 小时候大家肯定都会见过或者玩过这个,砂画 就是给一个模板上面会有轮廓,然后用给好的彩砂颜料上色,画完之后还blingbling的,也是很好 ...

  5. 编写一个“绘画系统”

    在最后一次的作业中,老师让我们编写一个"绘画系统",提供一系列绘画材料(例如画笔/颜料/滤镜)给用户操作,以创作出动态/交互的绘画作品.这个绘画系统是对"绘画" ...

  6. 基于Processing和Leap Motion的绘画系统

    对传统的绘画系统的思考 一说到绘画,大家脑海中出现的画面应该差不多都是画家和他手中的笔还有面前的画板吧.确实,在传统的绘画系统中,各种各样的笔和纸是画家们最重要的创作工具.不管是铅笔.毛笔,还是蜡笔. ...

  7. 绘画系统的简单实现(p5.js)

    绘画定义 搜狗词条给了"绘画"一个很有意思的词----"猴子的艺术",这是在中世纪的欧洲这样形容的,我觉得十分地贴切,因为如同猴子喜欢模仿人类活动一样,绘画也是 ...

  8. Processing大作业——绘画系统

    最终迎来了互动媒体最终的大作业,前两次我们分别讨论了,手绘和码绘的区别还有如何进行动态作画. 有兴趣的可以戳下方链接看一看我之前的两次作业. 第一次码绘VS手绘指路:https://blog.csdn ...

  9. 保姆级教程教你快速搭建属于自己的AI绘画系统!(收藏)

    Midjourney收费高.无法支付,且生成想要的图片受限,风格固定,如果能够10分钟快速搭建属于自己的AI绘画系统,并且对电脑配置无要求,你心动了吗? 先来看看AI系统构建完成后的画面,只需要输入自 ...

  10. 绘画系统-Processing版

    创作了两个版本的绘画系统,MATLAB版请移步https://blog.csdn.net/dont_like_jiemo/article/details/85345407 -------------- ...

最新文章

  1. JQuery元素选择器:和||,逻辑选择
  2. 中国AI芯片公司霸榜谷歌Waymo自动驾驶算法挑战赛!五个赛道,四项冠军
  3. 2018危机与机遇丨PMCAFF年度精选合集
  4. Spring Boot-@ImportResource注解
  5. 服务器升级文件 不推送就无法打开吗,Win7升Windows10有获取通知,但是就不推送的解决方法...
  6. 通过JS函数劫持自定义百度云分享提取码
  7. node2vec python_论文笔记 | node2vec
  8. steam游戏直连工具
  9. oracle 同义词public,oracle中private同义词跟public同义词
  10. C盘无损扩容 win10(亲测,良心,有用)
  11. 从零开始做游戏 - 上上下下左右左右BABA
  12. miflash刷机:fastboot模式/保留数据刷机
  13. 使用ffmpeg合成哔哩哔哩m4s格式的音视频流为mp4
  14. 基于SVM算法的男女生分类器
  15. 被“Python之父”称为最强外挂 这个Python库没人敢说不好
  16. JDK 8 的安装报javac不是外部或内部命令
  17. 登陆人人网爬取个人信息
  18. 附PPT下载|网易大数据用户画像实践
  19. Quantum Espresso Hands-On实战训练(三)——DOS计算
  20. 什么是闭包?闭包的工作原理、优缺点、使用场景和对页面的影响

热门文章

  1. 学生选课系统—软件工程课程设计
  2. 参考 雷霄骅https://blog.csdn.net/leixiaohua1020/article/list/28
  3. android 音频文件下载
  4. java android实习报告,java和安卓实习报告.pdf
  5. C#cefsharp Winform
  6. Android - 浅谈 Handler 机制
  7. CASA方法估算NPP(IDL+ENVI)
  8. 植被净初级生产力(NPP)的计算
  9. 如何下载Django 离线文档?
  10. 日志易:IT 运维分析及海量日志搜索的实践之路(上)