如何做一個iOS分形App

介紹

在這個教程中绩衷,我們會做一個可以渲染Mandelbrot Set的應用程序,我們可以縮放和平鋪它來看分形那令人驚嘆的復雜之美喷好。最終的結果如下:

著色程序的代碼

void main() {

#define iterations 128

vec2 position = v_tex_coord; // gets the location of the current pixel in the intervals [0..1] [0..1]

vec3 color = vec3(0.0,0.0,0.0); // initialize color to black

vec2 z = position; // z.x is the real component z.y is the imaginary component

// Rescale the position to the intervals [-2,1] [-1,1]

z *= vec2(3.0,2.0);

z -= vec2(2.0,1.0);

vec2 c = z;

float it = 0.0; // Keep track of what iteration we reached

for (int i = 0;i < iterations; ++i) {

// zn = zn-1 ^ 2 + c

// (x + yi) ^ 2 = x ^ 2 - y ^ 2 + 2xyi

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += c;

if (dot(z,z) > 4.0) { // dot(z,z) == length(z) ^ 2 only faster to compute

break;

}

it += 1.0;

}

if (it < float(iterations)) {

color.x = sin(it / 3.0);

color.y = cos(it / 6.0);

color.z = cos(it / 12.0 + 3.14 / 4.0);

}

gl_FragColor = vec4(color,1.0);

}

你可以下載起始版本跟著教程一起做,也可以在本文結尾找到最終版本的代碼。

項目設置

Gamescene.sks文件里包含一個名為fractal的子畫面围橡,它填充了整個界面并且著色程序程序Fractal.fsh也附在它上。

Fractal.fsh包含了上面著色程序的代碼

GameViewController.swift包含了設置游戲場景的代碼

GameScene.swift為空

如果你現(xiàn)在運行代碼缕贡,你將會得到如下的結果:

請注意縱橫比固定為3/2翁授,我們需要先根據(jù)屏幕大小調節(jié)它。

并且由于畫面是靜態(tài)的晾咪,所以你不可能與它有任何方式的交互收擦。

設置界面

我們將用一個透明的scrollview來處理平鋪縮放。scrollview將自動跟蹤我們的位置以及我們在分形中的縮放程度谍倦。

打開`Main.storyboard`文件炬守,拖進去一個scrollview。將scrollview設置成fill the view剂跟,并對它的寬度减途,到頂部距離,到底部距離設置限制曹洽。

將scrollview的最大縮放程度設置為100000鳍置,意味著我們將可以把分享放大到十萬倍!我們不能再放大更多了因為已經接近了`float`類型的準確極限送淆。

拖一個view(畫面)到scrollview里税产,它將用作處理縮放。這個view本身不會展示任何東西偷崩,我們將用到它的contentOffset和scrollView的zoom屬性來更新我們的著色程序辟拷。要確保這個畫面可以填滿scrollView,并且設定好寬度,到頂部底部左右距離的限制阐斜。將畫面的背景色設置為 Clear Color (透明色)衫冻。

接下來我們將連接我們所需要的outlet和scrollView的代理。

給scrollView和scrollView的contentView拖進outlet谒出。

class GameViewController: UIViewController, UIScrollViewDelegate? {

@IBOutlet weak var contentView: UIView!

@IBOutlet weak var scrollView: UIScrollView!

...

}

接下來我們去掉代理方法隅俘,并且實現(xiàn)viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView?這個方法

class GameViewController: UIViewController, UIScrollViewDelegate? {

...

func scrollViewDidScroll(scrollView: UIScrollView) {

}

func scrollViewDidZoom(scrollView: UIScrollView) {

}

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {

return contentView

}

...

}

向著色程序發(fā)送數(shù)據(jù)

著色程序可以從你的swift代碼里的uniform變量里獲得數(shù)據(jù)邻奠。uniform變量可以在SpriteKit編輯器里聲明。那現(xiàn)在我們來聲明一下uniform變量为居。

打開GameScene.sks文件碌宴,選擇 mandelbrote sprite。將insepctor拖到底部蒙畴,在“Custom shader Uniforms”里添加兩項:float類型的zoom贰镣,值為1, 以及vec2類型的offset膳凝。我們將用這兩項uniform變量儲存scrollView的contentOffset以及zoom屬性八孝。

警告:Xcode 6.3的uniform變量有bug。它不能直接在編輯器里賦值初始化鸠项,你必須在代碼里初始化它們干跛。

我們可以通過shader屬性來獲取節(jié)點上(node)著色程序,用theuniformedName()方法來從著色程序得到uniform變量祟绊。以下是我們獲取zoom uniform變量的例子:

let zoomUniform = node.shader!.uniformNamed("zoom")!

Once we have a uniform we can change its value via one of of the properties

當我們有了uniform變量后楼入,我們可以通過它的屬性來改變它的值。

var textureValue: SKTexture!

var floatValue: Float

var floatVector2Value: GLKVector2

var floatVector3Value: GLKVector3

var floatVector4Value: GLKVector4

var floatMatrix2Value: GLKMatrix2

var floatMatrix3Value: GLKMatrix3

var floatMatrix4Value: GLKMatrix4

We’re only interested in usingfloatValueandfloatVector2Valuefor this tutorial.

在本教程里牧抽,我們只對floatValue和floatVector2Value感興趣嘉熊。

Ex: to set the zoom to 2 we use

例子:將zoom的值設置成2

zoomUniform.floatValue = 2

Coordinate systems and mapping intervals

坐標系以及映射出區(qū)間

我們將在保持比例的基礎上映射不同的坐標系。我們將用這個來轉化scrollview的坐標到復平面扬舒。

讓我們先看一下一維的情況:

將x從區(qū)間[0,a]映射到區(qū)間[0,1]阐肤,我們只需要除以區(qū)間長度x' = x / a。

將x從區(qū)間[0,1]映射到區(qū)間[a,b]讲坎,我們可以乘上區(qū)間長度孕惜,然后再加上區(qū)間起始值,x' = x * (b - a) + a晨炕。

舉個例子衫画,比如iPhone4的x坐標,x坐標為0到480之間瓮栗。映射x到[0削罩,1], 我們用x' = x / 480。映射x'從[0,1]到[-2,2]费奸,我們用x'' = x' * 4 - 2

如果我們屏幕上有一點x弥激,坐標值為120,那么對應到區(qū)間[0,1]將成為120 / 480 = 0.25愿阐,以及在區(qū)間[-2,2]微服,如下所見它將成為0.25 * 4 - 2 = -1。

Mapping between the scrollview and the complex plane

scrollView及復平面互相映射

我們需要講scrollView上的點轉換到復平面换况。第一步职辨,先將scrollView上的點轉換到區(qū)間[0,1]盗蟆。通過將contentOffset除以contentSize可以將contentOffset轉換到區(qū)間[0,1]戈二。

var offset = scrollView.contentOffset

offset.x /= scrollView.contentSize.width

offset.y /= scrollView.contentSize.height

我們著色程序x舒裤,y坐標都有點在區(qū)間[0,1],所以我們要在scrollView的contentView里映射出這些店觉吭。

標準化過的contentView為1.0 / zoom腾供,所以contentView里標準化過的點坐標講在區(qū)間[contentOffset / contentSize,contentOffset / contentSize + 1.0 / zoom]。

還有我們必須牢記的是鲜滩,y軸的點在GLSL上伴鳖,而點(0,0)在左下角,所以我們必須翻轉y軸來對應我們的scrollView徙硅。

下面的GLSL代碼轉換scrollView的contentView里點的位置榜聂。

// Fractal.fsh

void main {

vec2 position = v_tex_coord;

position.y = 1.0 - position.y; // flip y coordinate

vec2 z = offset + position / zoom;

...

}

如下你可以看見藍色的scrollView的contentView在標準化與未標準化過的邊框。contentSize = (960,640)嗓蘑,contentOffset = (240,160)须肆,zoom = 2.0

ScrollView

標準化過的ScrollView

最后我們將點映射到復平面。為了在mandelbrot里得到好看的效果桩皿,我們將希望映射區(qū)域[-1.5,0.5] x [-1,1]復平面豌汇。

我們還想使縱橫比正確。現(xiàn)在我們的x泄隔、y軸的比例一樣拒贱,我們要乘以x和縱橫比使得圖片不會變形。

縱橫比是什么

縱橫比是屏幕寬度和高度的比例佛嬉。

// Fractal.fsh

void main {

...

z *= 2.0;

z -= vec2(1.5,1.0);

float aspectRatio = u_sprite_size.x / u_sprite_size.y;

z.x *= aspectRatio;

...

}

下面你可以看到我們scrollView的contentView映射到的平復面以及糾正過縱橫比的結果逻澳。

為了整合上面所有代碼,我們建了一個新的方法叫updateShader,它可以傳一個contentView坐標到著色程序暖呕。我們所需要做的就是在scrollView的代理方法里調用updateShader方法赡盘。

class GameViewController: UIViewController, UIScrollViewDelegate? {

...

func updateShader(scrollView: UIScrollView) {

let zoomUniform = node.shader!.uniformNamed("zoom")!

let offsetUniform = node.shader!.uniformNamed("offset")!

var offset = scrollView.contentOffset

offset.x /= scrollView.contentSize.width

offset.y /= scrollView.contentSize.height

zoomUniform.floatValue = Float(scrollView.zoomScale)

offsetUniform.floatVector2Value = GLKVector2Make(Float(offset.x), Float(offset.y))

}

func scrollViewDidScroll(scrollView: UIScrollView) {

updateShader(scrollView)

}

func scrollViewDidZoom(scrollView: UIScrollView) {

updateShader(scrollView)

}

...

}

同時也別忘了當view出現(xiàn)時調用updateShader方法,這樣你才可以初始化uniform變量缰揪。

class ViewController {

...

override func viewDidAppear(animated: Bool) {

super.viewDidAppear(animated)

updateShader(scrollView)

}

...

}

最終著色程序的如下所示:

void main() {

#define iterations 128

vec2 position = v_tex_coord; // gets the location of the current pixel in the intervals [0..1] [0..1]

position.y = 1.0 - position.y;

vec2 z = offset + position / zoom;

z *= 2.0;

z -= vec2(1.5,1.0);

float aspectRatio = u_sprite_size.x / u_sprite_size.y;

z.x *= aspectRatio;

vec2 c = z;

float it = 0.0; // Keep track of what iteration we reached

for (int i = 0;i < iterations; ++i) {

// zn = zn-1 ^ 2 + c

// (x + yi) ^ 2 = x ^ 2 - y ^ 2 + 2xyi

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += c;

if (dot(z,z) > 4.0) { // dot(z,z) == length(z) ^ 2 only faster to compute

break;

}

it += 1.0;

}

vec3 color = vec3(0.0,0.0,0.0); // initialize color to black

if (it < float(iterations)) {

color.x = sin(it / 3.0);

color.y = cos(it / 6.0);

color.z = cos(it / 12.0 + 3.14 / 4.0);

}

gl_FragColor = vec4(color,1.0);

}

Complete Source Code

完整代碼

挑戰(zhàn)

1 . 優(yōu)化

黑色部分渲染的最慢陨享。幸好根據(jù)下圖,我們可以很快知道一個點是否在兩塊黑色部分之一里 (心形部分或者區(qū)域2)钝腺。這里你可以找到如何判斷點是否在兩塊黑色區(qū)域之一里的方法抛姑。加上這些代碼來改進著色程序,它們只會在點不在這兩個區(qū)域里執(zhí)行mandelbrot循環(huán)艳狐。這將大幅度提高app在這些區(qū)域可見時的表現(xiàn)定硝。

見下圖,主要的心形為紅色毫目,區(qū)域2為綠色蔬啡。

Hint

提示

只當點在這些區(qū)域中的一個以外的時候執(zhí)行mandelbrot循環(huán)诲侮。

Solution

答案

void main() {

#define iterations 128

vec2 position = v_tex_coord; // gets the location of the current pixel in the intervals [0..1] [0..1]

position.y = 1.0 - position.y;

vec2 z = offset + position / zoom;

z *= 2.0;

z -= vec2(1.5,1.0);

float aspectRatio = u_sprite_size.x / u_sprite_size.y;

z.x *= aspectRatio;

vec2 c = z;

bool skipPoint = false;

//? ? cardioid checking

if ((z.x + 1.0) * (z.x + 1.0) + z.y * z.y < 0.0625) {

skipPoint = true;

}

//? ? period 2 checking

float q = (z.x - 0.25) * (z.x - 0.25) + z.y * z.y;

if (q * (q + (z.x - 0.25)) < 0.25 * z.y * z.y) {

skipPoint = true;

}

float it = 0.0; // Keep track of what iteration we reached

if (!skipPoint) {

for (int i = 0;i < iterations; ++i) {

// zn = zn-1 ^ 2 + c

// (x + yi) ^ 2 = x ^ 2 - y ^ 2 + 2xyi

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += c;

if (dot(z,z) > 4.0) { // dot(z,z) == length(z) ^ 2 only faster to compute

break;

}

it += 1.0;

}

}

vec3 color = vec3(0.0,0.0,0.0); // initialize color to black

if (it < float(iterations) && !skipPoint) {

color.x = sin(it / 3.0);

color.y = cos(it / 6.0);

color.z = cos(it / 12.0 + 3.14 / 4.0);

}

gl_FragColor = vec4(color,1.0);

}

完整代碼

2 . 做一個類似的app,可以讓你探索Julia set 的某點c箱蟆。

例子: vec2 c = vec2(-0.76, 0.15);

Solution

答案

void main() {

#define iterations 128

vec2 position = v_tex_coord; // gets the location of the current pixel in the intervals [0..1] [0..1]

position.y = 1.0 - position.y;

vec2 z = offset + position / zoom;

z *= 2.0;

z -= vec2(1.0,1.0);

float aspectRatio = u_sprite_size.x / u_sprite_size.y;

z.x *= aspectRatio;

vec2 c = vec2(-0.76, 0.15);

float it = 0.0; // Keep track of what iteration we reached

for (int i = 0;i < iterations; ++i) {

// zn = zn-1 ^ 2 + c

// (x + yi) ^ 2 = x ^ 2 - y ^ 2 + 2xyi

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += c;

if (dot(z,z) > 4.0) { // dot(z,z) == length(z) ^ 2 only faster to compute

break;

}

it += 1.0;

}

vec3 color = vec3(0.0,0.0,0.0); // initialize color to black

if (it < float(iterations)) {

color.x = sin(it / 3.0);

color.y = cos(it / 6.0);

color.z = cos(it / 12.0 + 3.14 / 4.0);

}

gl_FragColor = vec4(color,1.0);

}

完整代碼

3 . 添加一個點c的uniform變量沟绪,使用戶可以用兩個手指改變其值。

提示

用UIPanGestureRecognizer來檢測兩個手指的范圍空猜。你需要標準化手勢識別器傳來的結果绽慈。

答案

class GameViewController: UIViewController, UIScrollViewDelegate {

...

var c: GLKVector2 = GLKVector2Make(0, 0)

override func viewDidLoad() {

...

let panGr = UIPanGestureRecognizer(target: self, action: "didPan:")

panGr.minimumNumberOfTouches = 2

view.addGestureRecognizer(panGr)

}

func didPan(panGR: UIPanGestureRecognizer) {

var translation = panGR.translationInView(view)

translation.x /= view.frame.size.width

translation.y /= view.frame.size.height

c = GLKVector2Make(Float(translation.x) + c.x, Float(translation.y) + c.y)

let cUniform = node.shader!.uniformNamed("c")!

cUniform.floatVector2Value = c

panGR.setTranslation(CGPointZero, inView: view)

}

}

完整代碼

4 . 用一個圖片來給julia分形涂色。有很多方法都可以實現(xiàn)辈毯,其中有一個很有意思的方法如下:

每一次循環(huán)都從圖片里得到對應z的顏色坝疼。如果顏色不是透明的就跳出循環(huán)。

如果跑完所有循環(huán)谆沃,得到的顏色依舊不是透明的钝凶,那么就用它來填色對應的像素。

如果是透明的唁影,那么就用另外一個公式來填點的顏色耕陷。比如標準化過的循環(huán)次數(shù)。

下面是一個用兔子照片來填色的julia分形夭咬。

Hint

提示

你需要再添加一個Texture類型的uniform變量啃炸,命名為image。你可以用vec4 color = texture2D(image,p)來得到texture在p位置的顏色卓舵。

答案

class GameViewController: UIViewController, UIScrollViewDelegate {

...

override func viewDidLoad() {

...

let imageUniform = node.shader!.uniformNamed("image")!

imageUniform.textureValue = SKTexture(imageNamed: "bunny")

}

...

}

vec4 getColor(vec2 p) {

if (p.x > 0.99 || p.y > 0.99 || p.x < 0.01 || p.y < 0.01) {

return vec4(0.0);

}

return texture2D(image,p);

}

void main() {

#define iterations 128

vec2 position = v_tex_coord; // gets the location of the current pixel in the intervals [0..1] [0..1]

position.y = 1.0 - position.y;

vec2 z = offset + position / zoom;

z *= 2.0;

z -= vec2(1.0,1.0);

float aspectRatio = u_sprite_size.x / u_sprite_size.y;

z.x *= aspectRatio;

vec2 c = vec2(-0.76, 0.15);

vec4 color = vec4(0.0); // initialize color to black

float it = 0.0; // Keep track of what iteration we reached

for (int i = 0;i < iterations; ++i) {

// zn = zn-1 ^ 2 + c

// (x + yi) ^ 2 = x ^ 2 - y ^ 2 + 2xyi

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += c;

color = getColor(z);

if (dot(z,z) > 4.0 || color.w > 0.1) { // dot(z,z) == length(z) ^ 2 only faster to compute

break;

}

it += 1.0;

}

if (color.w < 0.1) {

float s = it / 80.0;

color = vec4(s,s,s,1.0);

}

gl_FragColor = color;

}

完整代碼

5 .類比分形南用,實驗一下Mandelbrot的公式。這個是開放性的挑戰(zhàn)掏湾。以下提供了兩個例子

燃燒之船的分形

Formulazn = abs(zn-12 + c)

公式zn = abs(zn-12 + c)

GLSL

GLSL

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += c;

z = abs(z);

源代碼

Sierpinski Julia

公式zn = zn-12 + 0.5 * c / (zn-12)

GLSLS

vec2 powc(vec2 z,float p) {

vec2 polar = vec2(length(z),atan(z.y,z.x));

polar.x = pow(polar.x,p);

polar.y *= p;

return vec2(polar.x * cos(polar.y),polar.x * sin(polar.y));

}

void main() {

...

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);

z += 0.5 * c * powc(z,-2.0);

...

}

源代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末裹虫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子融击,更是在濱河造成了極大的恐慌筑公,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尊浪,死亡現(xiàn)場離奇詭異匣屡,居然都是意外死亡,警方通過查閱死者的電腦和手機拇涤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門捣作,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鹅士,你說我怎么就攤上這事券躁。” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵也拜,是天一觀的道長以舒。 經常有香客問我,道長慢哈,這世上最難降的妖魔是什么蔓钟? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮岸军,結果婚禮上奋刽,老公的妹妹穿的比我還像新娘瓦侮。我一直安慰自己艰赞,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布肚吏。 她就那樣靜靜地躺著方妖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罚攀。 梳的紋絲不亂的頭發(fā)上党觅,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音斋泄,去河邊找鬼杯瞻。 笑死,一個胖子當著我的面吹牛炫掐,可吹牛的內容都是我干的魁莉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼募胃,長吁一口氣:“原來是場噩夢啊……” “哼旗唁!你這毒婦竟也來了?” 一聲冷哼從身側響起痹束,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤检疫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后祷嘶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屎媳,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年论巍,在試婚紗的時候發(fā)現(xiàn)自己被綠了烛谊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡环壤,死狀恐怖晒来,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情郑现,我是刑警寧澤湃崩,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布荧降,位于F島的核電站,受9級特大地震影響攒读,放射性物質發(fā)生泄漏朵诫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一薄扁、第九天 我趴在偏房一處隱蔽的房頂上張望剪返。 院中可真熱鬧,春花似錦邓梅、人聲如沸脱盲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钱反。三九已至,卻和暖如春匣距,著一層夾襖步出監(jiān)牢的瞬間面哥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工毅待, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尚卫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓尸红,卻偏偏與公主長得像吱涉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子驶乾,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容

  • 深入理解傅里葉變換Mar 12, 2017 這原本是我在知乎上對傅立葉變換邑飒、拉普拉斯變換、Z變換的聯(lián)系级乐?為什么要進...
    價值趨勢技術派閱讀 5,760評論 2 2
  • 我還是一個年輕的小人兒 可在很久很久以前 我就決定要在二十多歲的某一天 騎馬踏雪歸家 用一整個季節(jié)的寒梅見證我的清...
    bb53503536bd閱讀 159評論 0 1
  • 看了今天的晨讀素材我想到了我們國家正在提倡的工匠精神疙咸,工匠精神其實也是專注力的表現(xiàn),我們經常說“干一行愛一行”风科。 ...
    曹娜2017閱讀 173評論 0 1
  • 東海之濱的太陽總著急升起撒轮,冉冉的在蔚蘭的大海里晃悠著撩人的亮麗!停留在8樓的2路汽車緩緩啟動著,似乎還在纏綿于午夜...
    ZG那時花開閱讀 316評論 0 5
  • 最近老婆總是莫名其妙生氣 每次都能被我哄好 不但不生氣了 還會覺得 老公真的優(yōu)秀
    一條人文主義狗_閱讀 256評論 0 0